Tworzenie własnych potoków
Poza używaniem standardowych potoków, Angular pozwala tworzyć własne. Każda niestandardowa potoku musi:
- Posiadać dekorator
@Pipe
z metadanymi potoku, wśród których jest własnośćname
. Ta wartość zostanie wykorzystana do wywołania potoku. Musi to być poprawny identyfikator JavaScript. - Implementować metodę transformacji interfejsu
PipeTransform
. Ta metoda pobiera z potoku wartość oraz zmienną liczbę argumentów dowolnego typu i zwracają wartość przekształconą (piped
).
import { Pipe, PipeTransform } from '@angular/core';
const FILE_SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const FILE_SIZE_UNITS_LONG = ['Bytes', 'Kilobytes', 'Megabytes',
'Gigabytes', 'Pettabytes', 'Exabytes', 'Zettabytes', 'Yottabytes'];
@Pipe({
name: 'formatFileSize'
})
export class FormatFileSizePipe implements PipeTransform {
transform(sizeInBytes: number, longForm: boolean): string {
const units = longForm
? FILE_SIZE_UNITS_LONG
: FILE_SIZE_UNITS;
let power = Math.round(Math.log(sizeInBytes)/Math.log(1024));
power = Math.min(power, units.length - 1);
const size = sizeInBytes/Math.pow(1024, power); // size in new units
const formattedSize = Math.round(size * 100)/100; // keep up to 2 decimals
const unit = units[power];
return `${formattedSize} ${unit}`;
}
}
Każdy parametr rozdzielany dwukropkami w szablonie odwzorowuje jeden argument metody w tej samej kolejności.
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<div>
<p *ngFor="let f of fileSizes">{{ f | formatFileSize }}</p>
<p>{{ largeFileSize | formatFileSize:true }}</p>
</div>`
})
export class AppComponent {
fileSizes = [10, 100, 1000, 10000, 100000, 10000000, 10000000000];
largeFileSize = Math.pow(10, 15)
}
Zobacz przykład [0038]
Tworzenie potoków ze stanem
Potoki są domyślnie bezstanowe. Aby zadeklarować, że potok jest stanowy, ustawiamy właściwośćpure
dekoratora@Pipe
na wartość false. To ustawienie mówi systemowi wykrywania zmian Angular, aby sprawdzał wynik potoku w każdym cyklu, niezależnie od tego, czy wejście potoku uległo zmianie, czy nie (zmiany mogą dotyczyć stanu).
import { Pipe, PipeTransform, ChangeDetectorRef } from '@angular/core';
import { Observable, interval } from 'rxjs';
import { take } from 'rxjs/operators';
// zob. https://angular.io/guide/rx-library
// naive implementation assumes small number increments
@Pipe({
name: 'animateNumber',
pure: false
})
export class AnimateNumberPipe implements PipeTransform {
private currentNumber: number = null; // intermediary number
private targetNumber: number = null;
transform(targetNumber: number): string {
if (targetNumber !== this.targetNumber) {
this.currentNumber = this.targetNumber || targetNumber;
this.targetNumber = targetNumber;
const difference = this.targetNumber - this.currentNumber
const msecCounter = interval(100).pipe( take(difference) );
msecCounter.subscribe(n => {
this.currentNumber++;
});
}
return String(this.currentNumber);
}
}
Zobacz przykład[037]