在 AngularJS 中,我可以通过使用 ng-model 选项来消除模型的抖动。
ng-model-options="{ debounce: 1000 }"
如何在 Angular 中消除模型的抖动? 我试图在文档中搜索去抖动,但我找不到任何东西。
https://angular.io/search/#stq=debounce&stp=1
一个解决方案是编写我自己的 debounce 函数,例如:
import {Component, Template, bootstrap} from 'angular2/angular2'; // Annotation section @Component({ selector: 'my-app' }) @Template({ url: 'app.html' }) // Component controller class MyAppComponent { constructor() { this.firstName = 'Name'; } changed($event, el){ console.log("changes", this.name, el.value); this.name = el.value; } firstNameChanged($event, first){ if (this.timeoutId) window.clearTimeout(this.timeoutID); this.timeoutID = window.setTimeout(() => { this.firstName = first.value; }, 250) } } bootstrap(MyAppComponent);
还有我的html
<input type=text [value]="firstName" #first (keyup)="firstNameChanged($event, first)">
但我正在寻找一个内置函数,Angular中有一个吗?
更新为 RC.5
debounceTime()使用 Angular 2,我们可以在表单控件的observable 上使用 RxJS 操作符去抖动valueChanges:
debounceTime()
valueChanges
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](http://plnkr.co/edit/A8Ms99r7sazUeZS90z7K?p=preview)
上面的代码还包括一个如何限制窗口调整大小事件的示例,正如@albanx 在下面的评论中所要求的那样。
尽管上面的代码可能是 Angular 的做法,但效率不高。每次击键和每次调整大小事件,即使它们被去抖动和限制,都会导致更改检测运行。换句话说, 去抖动和节流不会影响更改检测运行的频率 。(我发现 Tobias Bosch 的GitHub 评论证实了这一点。)您可以在运行 plunker 时看到这一点,当您ngDoCheck()在输入框中键入或调整窗口大小时,您会看到调用了多少次。(使用蓝色的“x”按钮在单独的窗口中运行 plunker 以查看调整大小事件。)
ngDoCheck()
一种更有效的技术是自己从事件中创建 RxJS Observables,在 Angular 的“区域”之外。这样,每次触发事件时都不会调用更改检测。然后,在您的订阅回调方法中,手动触发更改检测——即,您控制何时调用更改检测:
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](http://plnkr.co/edit/E77SzTXvBnaBL4SzyOgh?p=preview)
我使用ngAfterViewInit()而不是ngOnInit()确保inputElRef已定义。
ngAfterViewInit()
ngOnInit()
inputElRef
detectChanges()将对此组件及其子组件运行更改检测。如果您希望从根组件运行更改检测(即运行完整的更改检测检查),请ApplicationRef.tick()改用。(我ApplicationRef.tick()在 plunker 的评论中打了电话。)请注意,调用tick()将导致ngDoCheck()被调用。
detectChanges()
ApplicationRef.tick()
tick()