Aktualisiert für RC.5
Mit Angular 2 können wir mit dem RxJS-Operator debounceTime()
auf dem valueChanges
Observable eines Formularsteuerelements entprellen :
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
Der obige Code enthält auch ein Beispiel für das Drosseln der Größe von Fenstern, wie von @albanx in einem Kommentar unten gefragt.
Obwohl der obige Code wahrscheinlich die Angular-Methode ist, ist er nicht effizient. Jeder Tastendruck und jedes Größenänderungsereignis führt dazu, dass die Änderungserkennung ausgeführt wird, obwohl sie entprellt und gedrosselt werden. Mit anderen Worten, das Entprellen und Drosseln hat keinen Einfluss darauf, wie oft die Änderungserkennung ausgeführt wird . (Ich habe einen GitHub-Kommentar von Tobias Bosch gefunden, der dies bestätigt.) Sie können dies sehen, wenn Sie den Plunker ausführen, und Sie sehen, wie oft ngDoCheck()
aufgerufen wird, wenn Sie in das Eingabefeld eingeben oder die Fenstergröße ändern. (Verwenden Sie die blaue Schaltfläche "x", um den Plunker in einem separaten Fenster auszuführen und die Größenänderungsereignisse anzuzeigen.)
Eine effizientere Technik besteht darin, RxJS Observables selbst aus den Ereignissen außerhalb von Angulars "Zone" zu erstellen. Auf diese Weise wird die Änderungserkennung nicht jedes Mal aufgerufen, wenn ein Ereignis ausgelöst wird. Lösen Sie dann in Ihren Abonnement-Rückrufmethoden die Änderungserkennung manuell aus, dh Sie steuern, wann die Änderungserkennung aufgerufen wird:
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
Ich benutze ngAfterViewInit()
statt ngOnInit()
um sicherzustellen, dass inputElRef
das definiert ist.
detectChanges()
führt die Änderungserkennung für diese Komponente und ihre untergeordneten Komponenten aus. Wenn Sie die Änderungserkennung lieber über die Stammkomponente ausführen möchten (dh eine vollständige Änderungserkennungsprüfung durchführen), verwenden Sie ApplicationRef.tick()
stattdessen. (Ich habe ApplicationRef.tick()
in Kommentaren im Plunker einen Anruf getätigt.) Beachten Sie, dass beim Aufruf ein Anruf ausgelöst tick()
wird ngDoCheck()
.