angular: Using NgIf with NgFor throws errors

When I add *ng-if to an element, which also has *ng-for, it is causing the variable to be set to null, even though the expression in the *ng-if is returning true. I am trying to only show the element if the expression returns true, and not show it at all otherwise.

Plunker reproducing the issue: http://plnkr.co/edit/ozJydSz1XqiNtrVCUfm1?p=preview

Is there a way I can do this?

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 1
  • Comments: 19 (9 by maintainers)

Most upvoted comments

There is a simple work around. In my case, I had an array of elements, but I didn’t always want to show them all.

Change this:

<select>
  <option *ngFor="#v of attr.values" *ngIf="v.selectable" [value]="v.id">{{ v.name }}</option>
</select>

To work like this:

<select>
  <template ngFor #v  [ngForOf]="attr.values">
    <option *ngIf="v.selectable" [value]="v.id" [selected]="attribute_selected[attr.id] == v.id">{{ v.name }}</option>
  </template>
</select>

What is the reasoning behind the limitation of one template directive per element? This is not the case in Angular 1.x and it seems like an unnecessary limitation IMO. It’s also an annoyance when converting from Angular 1.x to Angular 2.0 since you now have to change your markup to use a wrapper element.

There could be only one template directive per element (* directive)

// current - invalid
      <div *ng-if="showCategory()" *ng-for="#category of categories">
        Category: {{ category.displayName }}
      </div>

// working - extra div level
      <div *ng-if="showCategory()">
        <div *ng-for="#category of categories">
          Category: {{ category.displayName }}
        </div>
      </div>

// working - single div
      <template [ng-if]="showCategory()">
        <div *ng-for="#category of categories">
          Category: {{ category.displayName }}
        </div>
      </template>

The error is in your example but we should report an error here - could you check if there is already a pending issue & close if this is the case ?

I think, it would be very cool if directives are evaluated in the same order as they exist in the markup. For example:

<div *ng-for="#name of people; #isEven=even" *ng-if="isEven">
    {{ name }}
</div>

could be desugared into:

<template ng-for #name [ng-for-of]="people" #isEven="even">
  <template [ng-if]="isEven">
    <div>{{ name }}</div>  
  </template>
</template>   

while:

<div *ng-if="showNames" *ng-for="#name of people">
    {{ name }}
</div>

could be desugared into:

<template  [ng-if]="showNames">
  <template ng-for #name [ng-for-of]="people">
    <div>{{ name }}</div>  
  </template>
</template>   

Unfortunately, DOM NamedNodeList doesn’t guarantee to maintain any order. But maybe it’s one more reason to implement #4417.

What do you think? @vicb @thelgevold @mhevery @pkozlowski-opensource

Another way to solve probably the most common one is to improve the ngFor syntax

<div *ngFor="let layer of input.children.all() if input"></div>