Cập nhật cho RC.5
Với Angular 2, chúng ta có thể gỡ lỗi bằng toán tử RxJS debounceTime()
trên điều khiển biểu mẫu valueChanges
có thể quan sát được:
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
Mã ở trên cũng bao gồm một ví dụ về cách điều chỉnh thay đổi kích thước cửa sổ các sự kiện, như được hỏi bởi @albanx trong một bình luận bên dưới.
Mặc dù đoạn mã trên có lẽ là cách làm Angular, nhưng nó không hiệu quả. Mỗi tổ hợp phím và mọi sự kiện thay đổi kích thước, mặc dù chúng được phát hành và điều chỉnh, dẫn đến việc phát hiện thay đổi đang chạy. Nói cách khác, việc gỡ rối và điều chỉnh không ảnh hưởng đến tần suất phát hiện thay đổi . (Tôi tìm thấy một bình luận GitHub bởi Tobias Bosch mà xác nhận điều này.) Bạn có thể thấy điều này khi bạn chạy các plunker và bạn thấy bao nhiêu lần ngDoCheck()
được gọi khi bạn gõ vào hộp nhập hoặc thay đổi kích thước cửa sổ. (Sử dụng nút "x" màu xanh để chạy plunker trong một cửa sổ riêng để xem các sự kiện thay đổi kích thước.)
Một kỹ thuật hiệu quả hơn là tự tạo RxJS Observables từ các sự kiện, bên ngoài "khu vực" của Angular. Bằng cách này, phát hiện thay đổi không được gọi mỗi khi một sự kiện xảy ra. Sau đó, trong các phương thức gọi lại đăng ký của bạn, kích hoạt phát hiện thay đổi theo cách thủ công - tức là, bạn kiểm soát khi phát hiện thay đổi được gọi:
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
Tôi sử dụng ngAfterViewInit()
thay vì ngOnInit()
để đảm bảo rằng inputElRef
được xác định.
detectChanges()
sẽ chạy phát hiện thay đổi trên thành phần này và con của nó. Nếu bạn muốn chạy phát hiện thay đổi từ thành phần gốc (nghĩa là chạy kiểm tra phát hiện thay đổi đầy đủ), sau đó sử dụng ApplicationRef.tick()
thay thế. (Tôi đặt một cuộc gọi đến ApplicationRef.tick()
trong các bình luận trong plunker.) Lưu ý rằng việc gọi tick()
sẽ gây ra ngDoCheck()
được gọi.