Zaktualizowano dla RC.5
W Angular 2 możemy odrzucić za pomocą operatora RxJS debounceTime()
na valueChanges
obserwowalnej kontrolce formularza :
import {Component} from '@angular/core';
import {FormControl} from '@angular/forms';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/throttleTime';
import 'rxjs/add/observable/fromEvent';
@Component({
selector: 'my-app',
template: `<input type=text [value]="firstName" [formControl]="firstNameControl">
<br>{{firstName}}`
})
export class AppComponent {
firstName = 'Name';
firstNameControl = new FormControl();
formCtrlSub: Subscription;
resizeSub: Subscription;
ngOnInit() {
// debounce keystroke events
this.formCtrlSub = this.firstNameControl.valueChanges
.debounceTime(1000)
.subscribe(newValue => this.firstName = newValue);
// throttle resize events
this.resizeSub = Observable.fromEvent(window, 'resize')
.throttleTime(200)
.subscribe(e => {
console.log('resize event', e);
this.firstName += '*'; // change something to show it worked
});
}
ngDoCheck() { console.log('change detection'); }
ngOnDestroy() {
this.formCtrlSub.unsubscribe();
this.resizeSub .unsubscribe();
}
}
Plunker
Powyższy kod zawiera również przykład ograniczania zdarzeń związanych ze zmianą rozmiaru okna, zgodnie z pytaniem @albanx w komentarzu poniżej.
Chociaż powyższy kod jest prawdopodobnie sposobem Angulara, nie jest efektywny. Każde naciśnięcie klawisza i każde zdarzenie zmiany rozmiaru, nawet jeśli są usuwane i ograniczane, powoduje uruchomienie wykrywania zmian. Innymi słowy, debouncing i throttling nie wpływają na częstotliwość wykrywania zmian . (Znalazłem komentarz na GitHub autorstwa Tobiasa Boscha, który to potwierdza.) Możesz to zobaczyć po uruchomieniu plunkera i zobaczyć, ile razy ngDoCheck()
jest wywoływana, gdy wpiszesz w pole wprowadzania lub zmienisz rozmiar okna. (Użyj niebieskiego przycisku „x”, aby uruchomić plunker w osobnym oknie, aby zobaczyć zdarzenia zmiany rozmiaru).
Bardziej wydajną techniką jest samodzielne tworzenie RxJS Observables na podstawie zdarzeń, poza „strefą” Angulara. Dzięki temu wykrywanie zmian nie jest wywoływane za każdym razem, gdy wystąpi zdarzenie. Następnie w metodach wywołania zwrotnego subskrypcji ręcznie wyzwalaj wykrywanie zmian - tj. Kontrolujesz, kiedy wywoływane jest wykrywanie zmian:
import {Component, NgZone, ChangeDetectorRef, ApplicationRef,
ViewChild, ElementRef} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/throttleTime';
import 'rxjs/add/observable/fromEvent';
@Component({
selector: 'my-app',
template: `<input #input type=text [value]="firstName">
<br>{{firstName}}`
})
export class AppComponent {
firstName = 'Name';
keyupSub: Subscription;
resizeSub: Subscription;
@ViewChild('input') inputElRef: ElementRef;
constructor(private ngzone: NgZone, private cdref: ChangeDetectorRef,
private appref: ApplicationRef) {}
ngAfterViewInit() {
this.ngzone.runOutsideAngular( () => {
this.keyupSub = Observable.fromEvent(this.inputElRef.nativeElement, 'keyup')
.debounceTime(1000)
.subscribe(keyboardEvent => {
this.firstName = keyboardEvent.target.value;
this.cdref.detectChanges();
});
this.resizeSub = Observable.fromEvent(window, 'resize')
.throttleTime(200)
.subscribe(e => {
console.log('resize event', e);
this.firstName += '*'; // change something to show it worked
this.cdref.detectChanges();
});
});
}
ngDoCheck() { console.log('cd'); }
ngOnDestroy() {
this.keyupSub .unsubscribe();
this.resizeSub.unsubscribe();
}
}
Plunker
Używam ngAfterViewInit()
zamiast, ngOnInit()
aby upewnić się, że inputElRef
jest zdefiniowane.
detectChanges()
uruchomi wykrywanie zmian dla tego składnika i jego elementów podrzędnych. Jeśli wolisz uruchomić wykrywanie zmian ze składnika głównego (tj. Uruchomić pełne sprawdzenie wykrywania zmian), użyj ApplicationRef.tick()
zamiast tego. ( ApplicationRef.tick()
Wstawiłem call to w komentarzach w plunkerze.) Zauważ, że wywołanie tick()
spowoduje, że ngDoCheck()
zostaniesz wezwany.