Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

*ngIf and *ngFor on same element causing error

I'm having a problem with trying to use Angular's *ngFor and *ngIf on the same element.

When trying to loop through the collection in the *ngFor, the collection is seen as null and consequently fails when trying to access its properties in the template.

@Component({   selector: 'shell',   template: `     <h3>Shell</h3><button (click)="toggle()">Toggle!</button>      <div *ngIf="show" *ngFor="let thing of stuff">       {{log(thing)}}       <span>{{thing.name}}</span>     </div>   ` })  export class ShellComponent implements OnInit {    public stuff:any[] = [];   public show:boolean = false;    constructor() {}    ngOnInit() {     this.stuff = [       { name: 'abc', id: 1 },       { name: 'huo', id: 2 },       { name: 'bar', id: 3 },       { name: 'foo', id: 4 },       { name: 'thing', id: 5 },       { name: 'other', id: 6 },     ]   }    toggle() {     this.show = !this.show;   }    log(thing) {     console.log(thing);   }  } 

I know the easy solution is to move the *ngIf up a level but for scenarios like looping over list items in a ul, I'd end up with either an empty li if the collection is empty, or my lis wrapped in redundant container elements.

Example at this plnkr.

Note the console error:

EXCEPTION: TypeError: Cannot read property 'name' of null in [{{thing.name}} in ShellComponent@5:12] 

Am I doing something wrong or is this a bug?

like image 489
garethdn Avatar asked Jan 07 '16 14:01

garethdn


People also ask

Can we use ngFor and ngIf on the same element?

Use ngFor and ngIf on same element It's very common scenario where we want to repeat a block of HTML using ngFor only when a particular condition is true. i.e., if ngIf is true.So in this case to use *ngIf and *ngFor on same element, place the *ngIf on a parent element that wraps the *ngFor element.

Can we use two structural directive in a single HTML element?

Comparing Component and Structural Directives A component directive can be created multiple times, that is, every component in Angular will have a @Component decorator attached, while we cannot apply more than one structural directive to the same HTML element.

How can I use two ngFor in one div?

You can't use multiple *ngFor s in the same element as those are structural directives and angular handles them in a specific way (basically for each structural directive it creates an ng-template element which then holds the directive itself, you can read more about it here: https://angular.io/guide/structural- ...

What is the difference between ngFor and ngIf?

NgIf conditionally displays items by adding or removing them from the DOM depending on the condition. NgFor renders a list of items from iterable objects.


2 Answers

Angular v2 doesn't support more than one structural directive on the same element.
As a workaround use the <ng-container> element that allows you to use separate elements for each structural directive, but it is not stamped to the DOM.

<ng-container *ngIf="show">   <div *ngFor="let thing of stuff">     {{log(thing)}}     <span>{{thing.name}}</span>   </div> </ng-container> 

<ng-template> (<template> before Angular v4) allows to do the same but with a different syntax which is confusing and no longer recommended

<ng-template [ngIf]="show">   <div *ngFor="let thing of stuff">     {{log(thing)}}     <span>{{thing.name}}</span>   </div> </ng-template> 
like image 117
Günter Zöchbauer Avatar answered Sep 22 '22 03:09

Günter Zöchbauer


As everyone pointed out even though having multiple template directives in a single element works in angular 1.x it is not allowed in Angular 2. you can find more info from here : https://github.com/angular/angular/issues/7315

2016 angular 2 beta

solution is to use the <template> as a placeholder, so the code goes like this

<template *ngFor="let nav_link of defaultLinks"  >    <li *ngIf="nav_link.visible">        .....    </li> </template> 

but for some reason above does not work in 2.0.0-rc.4 in that case you can use this

<template ngFor let-nav_link [ngForOf]="defaultLinks" >    <li *ngIf="nav_link.visible">        .....    </li>  </template> 

Updated Answer 2018

With updates, right now in 2018 angular v6 recommend to use <ng-container> instead of <template>

so here is the updated answer.

<ng-container *ngFor="let nav_link of defaultLinks" >    <li *ngIf="nav_link.visible">        .....    </li>  </ng-container> 
like image 42
imal hasaranga perera Avatar answered Sep 21 '22 03:09

imal hasaranga perera