7/11/16

3 Different Ways To Use ngFor

Introduction

Like ng-repeat directive of angular 1. there is ngFor directive in angular2. it uses for display lists of items.
For example sake - lets take situation we need to display table of site users.
In old angular1 the template should look like this:
angular1 template


    <div>
      <h2>{{name}}</h2>
      <div  ng-if="userList.length===0">No Users Yet</div>
      <table ng-if="userList.length" >
        <tr><th>First Name</th><th>Last Name</th></tr>
        <!-- Look here -->
        <tr ng-repeat="user in userList">
         <td>
           {{user.firstName}}
         </td>
         <td>
           {{user.lastName}}
         </td>         
        </tr>
      </table>
    </div>

And here is controller & service code:
angular1 code

   //service 
   app.factory('usersSrv', function($http, $q) {
     function get(){
       var defer = $q.defer();
       $http.get('./users.json').success(function(data){
         defer.resolve(data)
       });
       return defer.promise;
     }
  
     var service = { 
        getUsers: get
     }

     return service;
   });
    //controller:
   $scope.userList = [];
   usersSrv.getUsers().then(function(users){
    $scope.userList = users; 
   })

Code: plunkr


The same scenario could be written in angular2 like this
template of app.component.ts

      <table *ngIf="userList.length">
        <tr>
            <th>First Name</th>
            <th>Last Name</th>
        </tr>
        <!-- Look here -->
        <tr *ngFor="let user of userList">
         <td>
           {{user.firstName}}
         </td>
         <td>
           {{user.lastName}}
         </td>         
        </tr>
      </table>

If there are some users in our db - they will be shown in table.
Currently the data taken from static array:

export class App {
  public userList:Array=[];
  constructor(userService:UserService) {
    this.name = 'Users Table:'
    this.userList =  [{firstName:'Vasia', lastName:'kozlov'}]
  }
}

we can see users table and everything is fine.

Code: plunk

Promise

The things starting to be more complicate when users collection is loaded from http request. It is complicate because users data now is asyncronous.
So, the first way to handle this thing is to use good old Prmoise known to us well from angular1.
Unlike angular1 we need to import toPromise operator of rxjs library.
users.servise.ts:


import { Injectable }    from '@angular/core';
import { Headers, Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';//import of "topromise" method from rxjs lib

@Injectable()
export class UserService {
  private url:string = './users.json';
  constructor(private http: Http) {}
  getUsers(): Promise {//method must return promise
    return this.http.get(this.url)
    .toPromise()
    .then(response => response.json());
  }
}

Now in the component code we able to use the "then" method for getting the callback of asyncronous method.
app.component.ts:

import {Component} from '@angular/core';
import {UserService} from './user.servise';
@Component({
  selector: 'my-app',
  providers: [],
  template: `
      ...
      ...
      ...
  `,
  providers: [UserService]
})
export class App {
  public userList:Array=[];
  constructor(userService:UserService) {
    this.name = 'Users Table:'
    userService.getUsers().then(//Here we can use "then"
      users => this.userList=users,
      error => console.log(error)
    );
  }
}

The good side here (for me) is that it very similar to angular1 way.
Code: plunk

Subscribe

The second attitude is to use map method of rxjs library.
users.servise.ts:


import { Injectable }    from '@angular/core';
import { Headers, Http } from '@angular/http';
import 'rxjs/Rx';

@Injectable()
export class UserService {
  private url:string = './users.json';
  constructor(private http: Http) {}
  getUsers() {
    return this.http.get(this.url).map(response => response.json());
  }
}

This map method returns Rx.Observable object, thus in component code we will need to use subscribe for handle the async data:
app.component.ts:

import {Component} from '@angular/core';
import {UserService} from './user.servise';
@Component({
      ...
      ...
      ...
  providers: [UserService]
})

export class App {
  public userList:Array=[];
  constructor(userService:UserService) {
    this.name = 'Users Table:';
    userService.getUsers().subscribe(
      users => this.userList=users,
      error=>console.log(error)
    );
  }
}

I like this way because it looks more shorter (no need to use conversion to promise like in previous way).
Code: plunk

Using Async Pipe

Unlike in angular1 there is a way in angular2 to pass observable collection directly to ngFor directive.
We can do this using angular2 async pipe.
template of app.component.ts


    <div>
      <h2>{{name}}</h2>
      <div  *ngIf="userList.length===0">No Users Yet</div>
      <table >
        <tr><th>First Name</th><th>Last Name</th></tr>
        <!-- Look here -->
        <tr *ngFor="let user of userList | async">
         <td>
           {{user.firstName}}
         </td>
         <td>
           {{user.lastName}}
         </td>         
        </tr>
      </table>
    </div>

Usinp async pipe makes the component code even more shorter: app.component.ts:

import {Component} from '@angular/core';
import {UserService} from './user.servise';
@Component({
      ...
      ...
      ...
  providers: [UserService]
})

export class App {
  public userList:Array=[];
  constructor(userService:UserService) {
    this.name = 'Users Table:';
    //no subscribe needed
    this.userList=userService.getUsers();
  }
}

Little More About Async Pipe

Another cool thing about this pipe is - that it can be used not only in ngFor
For example - if you need to show "No Users Yet" message when no users found in DB, you may use async pipe too:
template of app.component.ts


    <div>
      <h2>{{name}}</h2>
      
      <!-- HERE -->
      <div  *ngIf="(userList | async)?.length===0">No Users Yet</div>
      <table *ngIf="(userList | async)?.length>0">
        <tr><th>First Name</th><th>Last Name</th></tr>
        <tr *ngFor="let user of userList | async">
         <td>
           {{user.firstName}}
         </td>
         <td>
           {{user.lastName}}
         </td>         
        </tr>
      </table>
    </div>


Code: plunk

Hope you have fun reading...

Getting started with docker

It is very simple to get started usig docker. All you need to do-is download the docker desktop for your system Once you get docker syste...