Mis à jour pour RC.5
Avec Angular 2, nous pouvons anti-rebond en utilisant l'opérateur RxJS debounceTime()
sur l' valueChanges
observable d' un contrôle de formulaire :
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
Le code ci-dessus comprend également un exemple de la façon de limiter les événements de redimensionnement de la fenêtre, comme demandé par @albanx dans un commentaire ci-dessous.
Bien que le code ci-dessus soit probablement la manière angulaire de le faire, il n'est pas efficace. Chaque frappe et chaque événement de redimensionnement, même s'ils sont déboncés et limités, entraînent l'exécution de la détection des modifications. En d'autres termes, le rebond et la limitation n'affectent pas la fréquence d'exécution de la détection des modifications . (J'ai trouvé un commentaire GitHub de Tobias Bosch qui le confirme.) Vous pouvez le voir lorsque vous exécutez le plunker et vous voyez combien de fois ngDoCheck()
est appelé lorsque vous tapez dans la zone de saisie ou redimensionnez la fenêtre. (Utilisez le bouton bleu "x" pour exécuter le plunker dans une fenêtre séparée pour voir les événements de redimensionnement.)
Une technique plus efficace consiste à créer vous-même des observables RxJS à partir des événements, en dehors de la "zone" d'Angular. De cette façon, la détection des modifications n'est pas appelée à chaque fois qu'un événement se déclenche. Ensuite, dans vos méthodes de rappel d'abonnement, déclenchez manuellement la détection de changement - c'est-à-dire que vous contrôlez le moment où la détection de changement est appelée:
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
J'utilise ngAfterViewInit()
au lieu de ngOnInit()
pour m'assurer que inputElRef
c'est défini.
detectChanges()
exécutera la détection des modifications sur ce composant et ses enfants. Si vous préférez exécuter la détection des modifications à partir du composant racine (c'est-à-dire exécuter une vérification complète de la détection des modifications), utilisez à la ApplicationRef.tick()
place. (J'ai mis un appel à ApplicationRef.tick()
dans les commentaires dans le plunker.) Notez que l'appel tick()
provoquera ngDoCheck()
un appel.