Actualizado para RC.5
Con Angular 2 podemos eliminar el rebote usando el operador RxJS debounceTime()
en un valueChanges
observable de control de formulario :
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
El código anterior también incluye un ejemplo de cómo regular los eventos de cambio de tamaño de la ventana, como lo solicitó @albanx en un comentario a continuación.
Aunque el código anterior es probablemente la forma angular de hacerlo, no es eficiente. Cada pulsación de tecla y cada evento de cambio de tamaño, aunque se eliminen y se limiten, da como resultado la detección de cambios en ejecución. En otras palabras, la eliminación de rebotes y la limitación no afectan la frecuencia con la que se ejecuta la detección de cambios . (Encontré un comentario de GitHub de Tobias Bosch que confirma esto). Puedes ver esto cuando ejecutas el plunker y ves cuántas veces ngDoCheck()
se llama cuando escribes en el cuadro de entrada o cambias el tamaño de la ventana. (Use el botón azul "x" para ejecutar el plunker en una ventana separada para ver los eventos de cambio de tamaño).
Una técnica más eficiente es crear RxJS Observables usted mismo a partir de los eventos, fuera de la "zona" de Angular. De esta manera, la detección de cambios no se llama cada vez que se dispara un evento. Luego, en sus métodos de devolución de llamada de suscripción, active manualmente la detección de cambios, es decir, usted controla cuándo se llama a la detección de cambios:
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
Lo uso en ngAfterViewInit()
lugar de ngOnInit()
asegurarme de que inputElRef
esté definido.
detectChanges()
ejecutará la detección de cambios en este componente y sus hijos. Si prefiere ejecutar la detección de cambios desde el componente raíz (es decir, ejecutar una verificación de detección de cambios completa), utilice ApplicationRef.tick()
en su lugar. (Puse una llamada ApplicationRef.tick()
en los comentarios en el plunker). Tenga en cuenta que llamar tick()
hará ngDoCheck()
que se llame.