42

Edit: It looks like my main problem now is that I can't seem to display async data from an object. I have a promise containing the data object, and when I use

{{ data | async }}

it will display

[object Object]

The issue is, I want to be able to display all the different attributes; i.e, Name, Symbol, etc. In Angular 1, I would just use

{{ data.Name | async }}

but that doesn't work here, since the async pipe tries to resolve the data.Name promise, which doesn't exist. I want to resolve the data promise and then display the Name key from it. At the moment, I'm working on creating my own pipe to display a key from an async object, but I'm wondering if there's a built-in Angular 2 pipe or function to handle this!


I've created a StockService class that returns a Promise containing an object to my StockInfo class, which contains the HTML to be displayed. I want to display the name of this object in my HTML, but I can't seem to get it to display.

In my StockInfo constructor:

this.stock.getStockData(this.ticker, http).then(function(val) {
  this.data = val;

  this.name = new Promise<string>(function(resolve) {
    resolve(this.data.Name);
  });
});

where this.stock is the StockService object.

In my HTML:

<h2>{{name | async}}</h2>

I've tried a number of different arrangements before settling on this one. I want the StockService class to handle the data fetching and the StockInfo class to handle the display. In Angular 1, I would create a factory for fetching data and handle the data processing in the controller, but I'm not quite sure how to go about this in Angular 2.

Is there a way to get it to display, or are there better ways to design my code that I should look into? Thanks!

3
  • 1
    Have you tried using {{ (data | async).Name }} or {{ (data | async)['Name'] }} ? Commented Dec 19, 2015 at 21:34
  • Why do you make this.name into a promise instead of just writing this.name = this.data.name? Or is this just a contrived example? Commented Jul 12, 2016 at 7:08
  • @Pylinux I'd imagine it's a contrived example to allow for a minimal reproducible example. Commented Jul 19, 2017 at 1:09

7 Answers 7

86

You do not need any special pipe. Angular 2 suppport optional field. You just need to add ? in your object

{{ (data | async)?.name }}

or

{{(name | async)?}}
Sign up to request clarification or add additional context in comments.

3 Comments

Does | async work on promises? I thought it only worked on Observables?
async pipe accept either promise or observables. It's mention in the official Angular 2 documentation here. angular.io/docs/ts/latest/guide/pipes.html
"The safe navigation operator ? means that the field is optional and if undefined, the rest of the expression should be ignored" – from angular.io/cheatsheet
19

There's nothing wrong with the accepted answer above. But it becomes a hassle to append | async? when we need to display many properties of the object. The more convenient solution is as follows:

<div *ngIf="data | async as localData">
   <div> {{ localData.name }} </div>
   <div> {{ localData.property1 }} </div>
   <div> {{ localData.property2 }} </div>
</div>

1 Comment

Can this be done without the conditional behavior of *ngIf?
9

You can also use pluck from rxjs/observable:

{{ user.pluck("name") | async }}

Pluck Returns an Observable containing the value of a specified nested property from all elements in the Observable sequence. If a property can't be resolved, it will return undefined for that value.

1 Comment

Those finding this in the future, note that "pluck" has been deprecated
4

If you work with Observable you can display data like this way:

<div *ngIf="data | async; let _data">
   <h3>{{_data.name}}</h3>
</div>

or

<h3>{{(data | async).name}}</h3>

3 Comments

Please specify why this markup solves the problem, how it should be used and what mechanism makes it work. Considering linking some documentation.
I hope this helps you
What does smth mean? Please prefer real words in your answers.
2

I think you are making this too complex, and just need to do something like this.

this.name = 
  this.stock.getStockData(this.ticker, http)
  .then( val => val.Name )

and

<h2>{{name.Name | async}}</h2>

5 Comments

Thanks! This certainly makes things a lot simpler and I can't believe I didn't think to write it this way before, but the data still isn't displaying in the HTML. I've checked to make sure that getStockData is returning a Promise with the correct data, but I still can't get it to show up in the HTML.
are you certain that getStockData is calling resolve?
Yep; when I added a console.log to the monstrosity in my OP, the resulting value had all the data I wanted.
Interestingly enough, when I change the HTML to <h2>{{name | async}}</h2>, it will display [object Object], but trying to dereference the Name key leads to nothing showing.
Thanks Simon! This works, but there are maybe 20 key-value pairs in this Object, and I want to display all 20 of these in the page. The getStockData method makes a HTTP GET request, and I want to be able to get all this data with a single call to getStockData. Is there no way to store the results in an object?
2

So I ended up writing my own asynchronous key pipe. Huge thanks to Simon for helping guide me here.

import {Pipe} from 'angular2/core';

@Pipe({
    name: 'key',
    pure: false
})

export class KeyPipe {
    private fetchedPromise: Promise<Object>;
    private result: string;

    transform(value: Promise<Object>, args: string[]) {
        if(!this.fetchedPromise) {
            this.fetchedPromise = value
                .then((obj) => this.result = obj[args[0]] );
        }
        return this.result;
    }
}

Usage:

<h2>{{ data | key: 'Name' }}</h2>

Someone please comment if Angular has its own functions for resolving a key from an asynchronous object.

Comments

2

The OP asked for promises but in case people are using Observables, adapting @user2884505's answer, since pluck isn't directly available on observables as a method in recent versions of RxJS, you may have something like this :

import { Pipe, PipeTransform } from '@angular/core';

import { Observable } from 'rxjs';
import { pluck } from 'rxjs/operators';

@Pipe({
  name: 'asyncKey',
  pure: false
})
export class AsyncKeyPipe implements PipeTransform {
  private observable: Observable<Object>;
  private result: Object;

  transform(value: any, ...args: any[]): any {

    if (!this.observable) {
      this.observable = value.pipe(pluck(...args));
      this.observable.subscribe(r => this.result = r);
    }

    return this.result;
  }
}

And then, you can use it, even for nested keys :

{{ user$ | asyncKey: 'address' : 'street' }}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.