Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read object field from observable using async pipe

I want to gain a better understanding of when to use the async pipe in Angular.

I love the idea of having data appear asynchronously to the view by simply adding a pipe that subscribes to the data.

However, I have only ever seen and properly applied the async pipe on network request, or some other custom observable, that contains either an array of data or a single field (like an observable string).

For example:

Lets say I make an API call that returns an array of college students.

// This data is within a network response
$students = ...get...map... 
[
  {
    name: "Bob OConnel",
    id: 0,
    description: "young..."
  },
  {
    name: "Rick Luckard",
    id: 3,
    description: "young..."
  },
  {
    name: "James Wing",
    id: 2,
    description: "young..."
  }
]

This could be displayed in a template like so:

<tr *ngFor="let student of $students | async">
  <td>{{student.id}}</td>
  <td>{{student.name}}</td>
  <td>{{student.description}}</td>
</tr>

However, given the ole REST API philosophy. I have yet to ever make a production API call that returns just an array of data. There are always fields on the same level as the array of data. But they're there for a reason, I dont want to simply throw those extra fields away in a Mapping.

So instead of $students being an array, it would become:

// This data is within a network response
$students = ...get...map... 
{
  id: "69asdkasdkljasd6969"
  href: "someURLrepresentingTheDatasOrigins"
  length: 3
  items: [
    {
      name: "Bob OConnel",
      id: 0,
      description: "young..."
    },
    {
      name: "Rick Luckard",
      id: 3,
      description: "young..."
    },
    {
      name: "James Wing",
      id: 2,
      description: "young..."
    }
  ]
}

So this means that in the template, I can throw an async pipe on the $students observable, but not on the fields within it.

This obviously doesnt work, because the fields within the observables are observables:

{{$students.id| async}}
// or
<tr *ngFor="let student of $students.items | async">...</tr>

So, does that mean that on network responses like that, I should map the values of the response out into their own individual observables if I want to asynchronously bind to them in a template? Is there any way around that if I dont want the network response to split into a million smaller objects (Or in the case of my example: {id, length, href} and {items}).

It feels as if I am missing something obvious about the usage of observables that would clear up this issue for me. Let me know if my question is not clear enough.

UPDATE I found something i didnt know I could do here: https://toddmotto.com/angular-ngif-async-pipe

Super helpful for situations where some sort of loading indicator should be in the place of async data. It also works to consolidate subscriptions into one place. That would be a matter of preference, but I think in many of my situations I would prefer to throw the subscription into an *ngIf.

Unless you can do that same else template logic into an *ngFor..?

like image 720
Pezetter Avatar asked Feb 13 '18 21:02

Pezetter


1 Answers

You don’t need to map values into their own individual observable. Instead you can simply wrap your observable in parentheses along with async pipe, and then reach fields inside the object returned by observable.

Try:

<tr *ngFor="let student of ($students | async).items">
  <td>{{student.id}}</td>
  <td>{{student.name}}</td>
  <td>{{student.description}}</td>
</tr>
like image 159
Maciej Treder Avatar answered Sep 21 '22 05:09

Maciej Treder