Pipe Hype



Pipe Hype

1 1


ng-conf-pipes


On Github Yonet / ng-conf-pipes

Pipe Hype

@AysegulYonet

Pipes

@Component({
selector: 'file-detail',
template: `
  <span>Last Modified:</span>
  <p>{{file?.lastModified | date}}</p>

  `
})
class FileDetailComponent { }
We still have the familiar sytax of pipe in our templates

Pipes Parameters

@Component({
selector: 'file-detail',
template: `
  <span>Last Modified:</span>
  <p>{{file?.lastModified | date:"MM/dd/yy"}}</p>

`
})
class FileDetailComponent { }
A pipe may accept any number of optional parameters to fine-tune its output.

Pipes Parameters

@Component({
selector: 'file-detail',
template: `
  <span>Last Modified:</span>
  <p>{{file?.lastModified | date:"MM/dd/yy"}}</p>
  <input [(ngModel)]="format">
`
})
class FileDetailComponent { }
The parameter value can be any valid template expression such as a string literal or component property. In other words, we can control the format through a binding

ReplacePipe

@Component({
...
template: `
  <span>Last Modified:</span>
  <p>{{ expression | replace:pattern:replacement}}</p>
`
})

class FileDetailComponent { }

Plunker

Can we write our own parameters with regex? Replace pipe Creates a new String with some or all of the matches of a pattern replaced by * a replacement.

ReplacePipe

@Component({
  template: `
  <span>Made with:</span>
  <p>Hello {{name | replace:pattern:'2.0'}}</p>`})
  class ReplacePipeComponent {
    constructor() {
	  this.name = 'Angular 1.5'
	  this.pattern = new RegExp(/(?:\d*\.)?\d+/g);
	}
  }
https://plnkr.co/edit/vYDCnojcr3PizzQbZNjN?p=preview

JsonPipe

@Component({
selector: 'file-detail',
template: `
  <span>File:</span>
  <p>{{file | json}}</p>
`
})
class FileDetailComponent { }
Cool new addition to angular 2 is json pipe, it is very helps with debugging. Still angular pipes are very limited in number, we will end up writing our custom pipes. Let's get to it.

Custom Pipes

Custom Pipes

import {Pipe,PipeTransform} from 'angular2/core'
@Pipe({name: 'trim'})
export class TrimPipe implements PipeTransform {
  transform(str:string, [length]) : string {
	if (!str || !length || str.length <= length) {
	  return (str || '');
	}
    let dots = length <= 3 ? '' : '...';
    return str.substr(0, length) + dots;
  }
}
		
A pipe is a class decorated with pipe metadata. We implement the transform method of PipeTransform interface. The @Pipe decorator takes an object with a name property whose value is the pipe name that we'll use within a template expression. Then we can define the transform method to format our input to our desired output.

Custom Pipes

import {Pipe,PipeTransform} from 'angular2/core'
@Pipe({name: 'trim'})
export class TrimPipe implements PipeTransform {
  transform(str:string, [length]) : string {
    if (!str || !length || str.length <= length) {
      return (str || '');
    }
    let dots = length <= 3 ? '' : '...';
      return str.substr(0, length) + dots;
  }
}
        
A pipe is a class decorated with pipe metadata. We implement the transform method of PipeTransform interface. The @Pipe decorator takes an object with a name property whose value is the pipe name that we'll use within a template expression. Then we can define the transform method to format our input to our desired output.

Custom Pipes

import {Pipe,PipeTransform} from 'angular2/core'
@Pipe({name: 'trim'})
export class TrimPipe implements PipeTransform {
  transform(str:string, [length]) : string {
    if (!str || !length || str.length <= length) {
      return (str || '');
    }
    let dots = length <= 3 ? '' : '...';
    return str.substr(0, length) + dots;
  }
}
        
A pipe is a class decorated with pipe metadata. We implement the transform method of PipeTransform interface. The @Pipe decorator takes an object with a name property whose value is the pipe name that we'll use within a template expression. Then we can define the transform method to format our input to our desired output.

Pipes and Change Detection

import {Pipe,
PipeTransform} from 'angular2/core';
@Pipe({name: 'shared'})
export class SharedPipe implements PipeTransform {
  transform(files:File[]) {
  return files.filter(file => file.shared);
}
}
		
What makes angular2 pipes more efficient is the way it detects change. By default angular2 pipes are pure. What is a pure pipe?

Pipes and Change Detection

<input type=checbox> [(ngModel)]="file.shared" {{file.name}}
The object reference to the array hasn't changed. It's the same array. That's all Angular cares about. From its perspective, same array, no change, no display update.

Pure and Impure Pipes

Pipes are pure by default.

Pure Pipes

Angular executes a pure pipe only when it detects a pure change to the input value.

class FilesComponent {
constructor(){...}
this.files.push(newFile);
}

A pure change is either a change to a primitive input value (String, Number, Boolean, Symbol) or a changed object reference (Date, Array, Function, Object).

Impure Pipes


@Pipe({name: 'shared', pure: false})
export class SharedPipe implements PipeTransform {
transform(files:File[]) {
return files.filter(file => file.shared);
}
}
Angular executes an impure pipe during every component change detection cycle. An impure pipe will be called a lot, as often as every keystroke or mouse-move.

AsyncPipe

import {Observable} from 'rxjs/Observable';

@Component(...)
class FilesListComponent {

  contacts: Observable<Array<File>>;

  constructor(filesService: FilesService) {
	this.files = filesService.getFiles();
  }
}
Angular AsyncPipe is an interesting example of an impure pipe. The Async pipe can receive a Promise or Observable as input and subscribe to the input changes automatically. It is also stateful. The pipe maintains a subscription to the input Observable and keeps delivering values from that Observable as they arrive.

AsyncPipe

import {Observable} from 'rxjs/Observable';

@Component(...)
class FilesListComponent {

    contacts: Observable<Array<Contact>>;

    constructor(filesService: FilesService) {
        this.files = filesService.getFiles();
    }
}
The Async pipe saves boilerplate in the component code. The component doesn't have to subscribe to the async data source, it doesn't extract the resolved values and expose them for binding, and the component doesn't have to unsubscribe when it is destroyed (a potent source of memory leaks).

AsyncPipe

@Component({
template:
  <ul>
    <li *ngFor="let file of files | async">
	  ...
    </li>
  </ul>
`
})
class FilesListComponent {...}

AsyncPipe

@Component({
template: `
  <ul>
	<li *ngFor="let file of files | async">
	  ...
	</li>
  </ul>
`
})
class FilesListComponent {...}
@Pipe({name:
'fetch'})
export class FetchJsonPipe implements PipeTransform{
  private fetched:any = null;
  constructor(private _http: Http) { }
  transform(url:string):any {
  this.fetched = null;
  this._http.get(url)
    .map( result => result.json() )
    .subscribe( result => this.fetched = result )
  }
  return this.fetched;
 }
}
You are in trouble if you make this inpure
<div *ngfor="let file of ('filesApiUrl' | fetch) | async">
{{file.name}}
</div>
This looks great but still not an ideal way

Performance Upgrade

Demo

Can we do better?

<div *ngfor="let file of userFileStore | async">
	{{file.name}}
</div>

Useful Resources

  • Falcor
  • GraphQl

Demo
Pipe Hype