126

In my Angular 2 component I have an Observable array

list$: Observable<any[]>;

In my Template I have

<div *ngIf="list$.length==0">No records found.</div>

<div *ngIf="list$.length>0">
    <ul>
        <li *ngFor="let item of list$ | async">item.name</li>
    </ul>
</div>

But list$.length doesn't work with in case of Observable array.

Update:

It seems that (list$ | async)?.length gives us the length but the below code still doesn't work:

<div>
    Length: {{(list$ | async)?.length}}
    <div *ngIf="(list$ | async)?.length>0">
        <ul>
            <li *ngFor="let item of (list$ | async)">
                {{item.firstName}}
            </li>
        </ul>
    </div>
</div>

Can anyone please guide how do I check length of Observable array.

tobias47n9e
  • 2,310
  • 3
  • 23
  • 46
Naveed Ahmed
  • 8,391
  • 11
  • 39
  • 75
  • Reported at https://github.com/angular/angular/issues/9641 – Günter Zöchbauer Jun 27 '16 at 17:08
  • 1
    Your approach here has another major issue: by leveraging the async pipe over and over in your template, you are actually kicking off that many subscriptions to the single Observable. KAMRUL HASAN SHAHED has the right approach above: Use the async pipe once and then provide an alias for the result that you can leverage in child nodes. – Harry Beckwith Jan 12 '19 at 22:32

7 Answers7

209

You can use the | async pipe:

<div *ngIf="(list$ | async)?.length==0">No records found.</div>

Update - 2021-2-17

<ul *ngIf="(list$| async) as list; else loading">
   <li *ngFor="let listItem of list">
      {{ listItem.text }}
   </li>
</ul>

<ng-template #loading>
  <p>Shows when no data, waiting for Api</p>
  <loading-component></loading-component>
</ng-template>

Update - Angular Version 6:

If you are loading up a css Skeleton you can use this. If the array has no items it will display the css template. If there is data then fill out the ngFor.

<ul *ngIf="(list$| async)?.length > 0; else loading">
   <li *ngFor="let listItem of list$| async">
      {{ listItem.text }}
   </li>
</ul>

<ng-template #loading>
  <p>Shows when no data, waiting for Api</p>
  <loading-component></loading-component>
</ng-template>
Günter Zöchbauer
  • 490,478
  • 163
  • 1,733
  • 1,404
  • 4
    I tried that as well but it gives error "TypeError: Cannot read property 'length' of null" – Naveed Ahmed Jun 27 '16 at 15:29
  • 3
    Hard to know from the information you provided. Try `
    No records found.
    ` (added `?`)
    – Günter Zöchbauer Jun 27 '16 at 15:30
  • 6
    I tried this and it works
    No records found.
    – Naveed Ahmed Jun 27 '16 at 15:32
  • 3
    The additional `?` is required because `list$` is only set *after* Angular2 tries to render the view the first time. `?` prevents the rest of the sub-expression to be evaluated until the part left of `?` becomes `!= null` (Elvis or safe-navigation operator). – Günter Zöchbauer Jun 27 '16 at 15:34
  • Yes that makes send, but the below code doesn't work:
    Length: {{(list$ | async)?.length}}
    0">
    • {{item.firstName}}
    Though the list length is printed (i.e. 20) but it doesn't render the list if placed under the ngIf. If I remove ngIf, it start working. Can you please guide?
    – Naveed Ahmed Jun 27 '16 at 16:02
  • Please have a look at http://plnkr.co/edit/5KBEVj5Hhtx0BB5v3vVg?p=preview if you remove
    0"> from todo.component, it will start displaying list.
    – Naveed Ahmed Jun 27 '16 at 17:11
  • @GünterZöchbauer is there a way to alias (list$ | async) with as then use alias.length? – JGFMK Aug 12 '17 at 17:36
  • @JGFMK Yes, that should work with recemt 4.x.x versions. – Günter Zöchbauer Aug 12 '17 at 18:26
  • @GünterZöchbauer Did you mean this? {{ (myArray | customPipe )?.length}} Only the link didn't take - I'll give it a try later - cheers – JGFMK Aug 15 '17 at 17:47
  • @JGFMK sorry https://stackoverflow.com/questions/44521254/get-length-of-array-in-ngfor-after-pipes-transformation – Günter Zöchbauer Aug 15 '17 at 19:56
  • How does it work with the `as` keyword: ``. Want to check the length so ion-list is hidden if uniqueTodayCollected.length == 0 – Xerri Jul 03 '19 at 11:21
  • Used this to fix: https://github.com/angular/angular/issues/9641#issuecomment-299167085 – Xerri Jul 03 '19 at 14:37
  • 1
    @GünterZöchbauer it looks to me, that first `async` pipe resolves data and therefore my next `async` pipe on loop doesn't display anything. Or maybe `*ngIf` creates an additional scope and therefore it is not working. Hard to tell. But while my loop is wrapped inside if, it doesn't display any data. If itself evaluates to `true` correctly. – Eugene Sep 06 '19 at 10:33
  • 1
    @GünterZöchbauer - in the above code you subscribe to the same resource using async pipe twice - it would be better to use `as` syntax: `
      0; else loading">
    • {{ listItem.text }}
    ` to store subscription in template variable `list` (pay attention to no dollar sign) and then use this one in the template - thus avoiding multiple subscriptions.
    – codeepic Jul 01 '20 at 10:19
  • You are right. `as` wasn't a thing yet back then when I posted the answer and I don't have the time anymore to keep my answers updated. – Günter Zöchbauer Jul 01 '20 at 10:54
  • 1
    will this not create 2 subscriptions? you could wrapp it `` and then `
      0; else loading">`
    – Mackelito Feb 17 '21 at 10:00
38

A solution for .ts-Files:

     this.list.subscribe(result => {console.log(result.length)});
Blank
  • 4,658
  • 2
  • 22
  • 24
20

For Angular 4+, try this

<div *ngIf="list$ | async;let list">
    Length: {{list.length}}
    <div *ngIf="list.length>0">
        <ul>
            <li *ngFor="let item of list">
                {{item.firstName}}
            </li>
        </ul>
    </div>
</div>
8

While this answer is correct

<div *ngIf="(list$ | async)?.length === 0">No records found.</div>

Keep in mind that if you are using http client to call backend (in most cases you do) you will get duplicate calls to your API if you have more that one list$ | async. This is because each | async pipe will create new subscriber to your list$ observable.

Andzej Maciusovic
  • 3,618
  • 1
  • 22
  • 36
3

This is what worked for me -

*ngIf="!photos || photos?.length===0"

I am getting my data from httpClient async.

All the other options here didn't work for me which was disappointing. Especially the sexy (list$ | async) pipe.

Basa..

Tzvi Gregory Kaidanov
  • 2,866
  • 1
  • 20
  • 29
3

Can be shortened as well:

<div *ngIf="!(list$ | async)?.length">No records found.</div>

Just use the exclamation mark before the parenthesis.

Daniyal Lukmanov
  • 969
  • 6
  • 19
-3

ionic 4

<div *ngIf="(items | async)?.length==0">No records found.</div>

it worked when i removed the $ sign

gehbiszumeis
  • 2,788
  • 4
  • 19
  • 33
  • `$` sign is used to denote Observable. It has nothing to do with working/logic. Its just a convention followed in Angular. – Anand Kumar Feb 04 '21 at 11:24