pipes used to transform output to template
        in sample's app.component an array of servers is instantiated
        NOTE : months are zer-based (go figure)
        
    ...
    export class AppComponent {
      servers = [
        {
          instanceType: 'medium',
          name: 'Production Server',
          status: 'stable',
          started: new Date(2017, 9, 13)
        },
        ...
      ];
      getStatusClasses(server: {instanceType: string, name: string, status: string, started: Date}) {
        return {
          'list-group-item-success': server.status === 'stable',
          'list-group-item-warning': server.status === 'offline',
          'list-group-item-danger': server.status === 'critical'
        };
      }
    }       
        
        in app.component built-in pipes are used to put the server name in uppercase and
        the date into a short format
        
    <ul class="list-group">
        <li class="list-group-item" *ngFor="let server of servers" [ngClass]="getStatusClasses(server)">
            <span class="badge">
            {{ server.status }}
            </span>
            <strong>{{ server.name }}</strong> | {{ server.instanceType | uppercase}} | {{ server.started | date}}
        </li>
    </ul> 
        
        
            Top
        
        Index
        
        
        generally parsing is done left to right do the order of pipes can be important
        this order will cause an error because the started property is a Date type while
        uppercase expects a string
        
    {{ server.started | uppercase | date: 'fullDate' }}
        
        
            Top
        
        Index
        
        
        create file named shorten.pipe.ts
        set up class implementing PipeTransform interface
        add Pipe decorator to class and set the pipe's name property
        
    import { Pipe, PipeTransform } from '@angular/core';
    @Pipe({
        name: 'shorten'
    })
    export class ShortenPipe implements PipeTransform {
        transform(value: any) {
            if (value.length > 10) {
                // return the first 10 chars
                return value.substr(0, 10) + ' ...';
            }
            return value;
        }
    }
        
        in app.module import ShortenPipe and add type to declarations
        
    ...
    import { ShortenPipe } from './shorten.pipe';
    @NgModule({
      declarations: [
        AppComponent, 
        ShortenPipe
      ],
      ...
    })
    export class AppModule { } 
        
        in app.component markup use the pipe
        
    <ul class="list-group">
        <li class="list-group-item" *ngFor="let server of servers" [ngClass]="getStatusClasses(server)">
            <span class="badge">
            {{ server.status }}
            </span>
        {{ server.name | shorten }} |   {{ server.instanceType | uppercase}} | {{ server.started | date: 'fullDate' | uppercase }}
        </li>
    </ul>
        
        only the first ten characters of the server name will be displayed
        if server name is less than ten characters no action will be taken
        
            Top
        
        Index
        
        
            parameterizing a custom pipe
        
 
        let pipe user determine how many characters to show
        
    ...
    export class ShortenPipe implements PipeTransform {
        transform(value: any, limit: number) {
            if (value.length > limit) {
                // return the first 10 chars
                return value.substr(0, limit) + ' ...';
            }
            return value;
        }
    } 
        
        to add argument to pipe
        
    <li class="list-group-item" *ngFor="let server of servers" [ngClass]="getStatusClasses(server)">
        <span class="badge">
        {{ server.status }}
        </span>
        {{ server.name | shorten: 15}}; | {{ server.instanceType | uppercase}} |   {{ server.started | date: 'fullDate' | uppercase }}
    </li>    
        
        if pipe's argument is omitted the pipe does nothing
        
            Top
        
        Index
        
        
            Example : creating a filter pipe
        
 
        to create a pipe using CLI
        
    ng g p <name of pipe>
        
        
    import { Pipe, PipeTransform } from '@angular/core';
    @Pipe({
      name: 'filter'
    })
    export class FilterPipe implements PipeTransform {
      transform(value: any, filterString: string, propName: string): any {
        if (value.length === 0 || filterString === '') {
          return value;
        }
        const resultArray = [];
        for (const item of value) {
          if (item[propName] === filterString) {
            resultArray.push(item);
          }
        }
        return resultArray;
      }
    }
        
        in the component markup the user can enter a status in the textbox
        that value is used as an argument to the pipe's transform method
        
    <input type="text" [(ngModel)]="filteredStatus" >
    <hr>
    <ul class="list-group">
        <li class="list-group-item" *ngFor="let server of servers | filter: filteredStatus: 'status'" [ngClass]="getStatusClasses(server)">
            <span class="badge">
            {{ server.status }}
            </span>
            {{ server.name | shorten: 15}} | {{ server.instanceType | uppercase}} | {{ server.started | date: 'fullDate' | uppercase }}
        </li>
    </ul>
        
        
            Top
        
        Index
        
        
        by default Angular does not apply pipes as data changes
        when the servers array is being filtered a newly added server will not appear in
        the list
        to change behavior add pure property to the pipe's decorator and set it to false
        doing so makes pipe 'recalculate' when any data on the page changes
        
    import { Pipe, PipeTransform } from '@angular/core';
    @Pipe({
      name: 'filter',
      pure: false
    })
    export class FilterPipe implements PipeTransform {
        ...
    }
        
        
            Top
        
        Index
        
        
            understanding the "async" pipe
        
 
        in app.component add a property which is a Promise
        
    ...
    export class AppComponent {
      appStatus = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve('stable');
        }, 2000)
      })
        ...
    }
        
        in the markup use string interpolation to display the new property
        
    <h4>App Status : {{ appStatus | async }}</h4>
        
        without the async pipe the property will be displayed as
        
    [object Promise]
        
        with the async pipe the property will be empty until the timeout expires and sets
        the property
        the async pipe can also be used with Observables
        
            Top
        
        Index