Atualizado para RC.5
Com o Angular 2, podemos rebater usando o operador RxJS debounceTime()
em um controle de formulário valueChanges
observável:
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
O código acima também inclui um exemplo de como acelerar eventos de redimensionamento de janelas, conforme solicitado por @albanx em um comentário abaixo.
Embora o código acima seja provavelmente a maneira angular de fazê-lo, ele não é eficiente. Cada pressionamento de tecla e cada evento de redimensionamento, mesmo que sejam debitados e regulados, resultam na execução da detecção de alterações. Em outras palavras, a rejeição e a limitação não afetam a frequência com que a detecção de alterações é executada . (Encontrei um comentário do Tobias Bosch no GitHub que confirma isso.) Você pode ver isso ao executar o desentupidor e quantas vezes ngDoCheck()
está sendo chamado quando você digita na caixa de entrada ou redimensiona a janela. (Use o botão azul "x" para executar o plunker em uma janela separada para ver os eventos de redimensionamento.)
Uma técnica mais eficiente é criar o RxJS Observables a partir dos eventos, fora da "zona" do Angular. Dessa forma, a detecção de alterações não é chamada sempre que um evento é disparado. Em seus métodos de retorno de chamada de inscrição, acione manualmente a detecção de alterações - ou seja, você controla quando a detecção de alterações é chamada:
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
Eu uso em ngAfterViewInit()
vez de ngOnInit()
garantir que inputElRef
está definido.
detectChanges()
executará a detecção de alterações neste componente e em seus filhos. Se você preferir executar a detecção de alterações no componente raiz (por exemplo, execute uma verificação completa da detecção de alterações), use-o ApplicationRef.tick()
. (Chamo ApplicationRef.tick()
nos comentários no desentupidor.) Observe que chamar tick()
fará com ngDoCheck()
que seja chamado.