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 li
s 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?
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.
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.
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- ...
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.
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>
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
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>
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With