Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Enhanced *ngIf and *ngFor in Angular Version 4.0.0

DZone's Guide to

Enhanced *ngIf and *ngFor in Angular Version 4.0.0

An Angular though leader checks out some of the new features of Angular 4, and shows you how to make the most of the improvements to this framework.

· Web Dev Zone ·
Free Resource

Deploying code to production can be filled with uncertainty. Reduce the risks, and deploy earlier and more often. Download this free guide to learn more. Brought to you in partnership with Rollbar.

Ever wanted an "else" statement in *ngIf ? It's finally here, together with some other nice improvements around dealing with Observables in templates. Let's explore them here.

If..Then..Else

The ngIf directive gets a nice improvement in Angular version 4.0.0. It’s been the target of many critiques even in AngularJS (v1.x) because of the lack of an “else” clause. In order to simulate if-then-else blocks in Angular templates, we had to use two ngIf directives with opposed boolean conditions.

<div *ngIf=”isLoggedIn()”>
   Hi, {{ user.name }}
</div>
<div *ngIf=”!isLoggedIn()”>
   You’re not logged in.
</div>

In Angular version 4, we now get an “else” instruction as part of the ngIf directive. We can thus transform the above template to the following:

<div *ngIf="isLoggedIn(); else notLoggedIn">
    Hi, {{ user.name }}!
</div>
<ng-template #notLoggedIn>
    You're not logged in.
</ng-template>

Better Observables Support in Templates

RxJS and Observables are already heavily being used within Angular, even when it comes to rendering async data into a template. When we execute an HTTP call with Angular, we get an Observable in return.

import { Component } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Component({
  selector: 'users-list',
  template: `
    <ul>
      <li *ngFor="let user of users">
        {{ user.username }}
      </li>
    </ul>
  `
})
export class UsersListComponent {
  users;

  constructor(private http: Http) {  }

  ngOnInit() {
    this.http
        .get('/api/users')
        .map(res => res.json())
        .subscribe((data) => {
          this.users = data;
        });
  }
}

In this example, we use the Observable’s subscribe function to register a callback for when the data is retrieved from our backend API. Once we have the data, we assign it to a local variable of our component, users, which in turn is data-bound on the ngFor in our template. The async pipe which is already present in Angular version 2, allows to write this in a more elegant way:

import { Component } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Component({
  selector: 'users-list',
  template: `
    <ul>
        <li *ngFor="let user of users$ | async">
            {{ user.username }}
        </li>
    </ul>
  `
})
export class UsersListComponent {
  users$;

  constructor(private http: Http) {  }

  ngOnInit() {
    this.users$ = this.http
        .get('/api/users')
        .map(res => res.json());
  }
}

We can directly assign the returned Observable to our users$ variable and bind it in our template. The Async Pipe (.. users$ | async) handles the subscription and unsubscription on the Observable for us, which makes it really convenient for directly binding asynchronous data in our templates.

Note, the “$” suffix in our variable name is simply a naming convention to communicate that this variable holds an Observable.

There’s one caveat though. We cannot access the collection within our template. Consider, for instance, if we wanted to enumerate the position of the rendered user entry in the collection with respect to the total number of entries.

Enumerate *ngFor Loops Using as and Async Pipes

While in version 2 we had to fallback to subscribing to the Observable in the component class, Angular version 4 now gives us the ability to handle such a scenario by assigning the async result from the Observable to a template variable: .. of users$ | async as users. A template variable is a variable declaration in our template, just like the user in our ngFor loop statement.

import { Component } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Component({
  selector: 'users-list',
  template: `
      <ul>
        <li *ngFor="let user of users$ | async as users; index as i">
          {{ user.username }} ({{ i }} of {{ users.length }})
        </li>
      </ul>
  `
})
export class UsersListComponent {
  users$;

  constructor(private http: Http) {  }

  ngOnInit() {
    this.users$ = this.http
        .get('/api/users')
        .map(res => res.json());
  }
}

Leveraging the as Keyword With *ngIf

Using the as keyword also works with ngIf. Within the ngIf expression, we can again directly use the Observable and assign it to a template variable. Hence, once the asynchronous call resolves, we can render that data in our template.

import { Component } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Component({
  selector: 'users-list',
  template: `
    <div *ngIf="user$ | async as user">
        Hi, {{ user.name }}!
    </div>
  `
})
export class UserLoginComponent {
  user$;

  constructor(private http: Http) {  }

  ngOnInit() {
    this.user$ = this.http
        .get('/api/auth/currentuser')
        .map(res => res.json());
  }
}


Conclusion

These two additions to the ngIf and ngFor directives make working with Observables directly within the templates a lot easier and more convenient.

Deploying code to production can be filled with uncertainty. Reduce the risks, and deploy earlier and more often. Download this free guide to learn more. Brought to you in partnership with Rollbar.

Topics:
web dev ,angular ,templates ,loops

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}