Using Pipes to Transform Output
parameterizing pipes
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

chaining multiple pipes
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

creating a custom pipe
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

pure & impure pipes
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

n4jvp.com