kon*_*vid 145 javascript angular
在AngularJS中,我可以使用ng-model选项去抖模型.
ng-model-options="{ debounce: 1000 }"
Run Code Online (Sandbox Code Playgroud)
如何在Angular中去抖模型?我试图在文档中搜索debounce,但我找不到任何东西.
https://angular.io/search/#stq=debounce&stp=1
一个解决方案是编写我自己的去抖函数,例如:
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);
Run Code Online (Sandbox Code Playgroud)
而我的HTML
<input type=text [value]="firstName" #first (keyup)="firstNameChanged($event, first)">
Run Code Online (Sandbox Code Playgroud)
但是我正在寻找一个内置函数,Angular中有一个吗?
Mar*_*cok 186
针对RC.5进行了更新
使用Angular 2,我们可以debounceTime()在窗体控件的valueChangesobservable 上使用RxJS运算符进行去抖:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
上面的代码还包含一个如何限制窗口调整大小事件的示例,如@albanx在下面的评论中所要求的那样.
虽然上面的代码可能是Angular方式,但效率不高.每次击键和每次调整大小事件,即使它们被去抖动和限制,也会导致变化检测运行.换句话说,去抖动和限制不会影响更改检测的运行频率.(我发现Tobias Bosch 的GitHub评论确认了这一点.)当你运行plunker时,你可以看到这个,当你ngDoCheck()输入输入框或调整窗口大小时,你会看到被调用了多少次.(使用蓝色的"x"按钮在单独的窗口中运行plunker以查看调整大小事件.)
更有效的技术是在Angular的"区域"之外的事件中自己创建RxJS Observable.这样,每次事件触发时都不会调用更改检测.然后,在您的订阅回调方法中,手动触发更改检测 - 即,您可以控制何时调用更改检测:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
我使用ngAfterViewInit()而不是ngOnInit()确保inputElRef定义.
detectChanges()将对此组件及其子组件运行更改检测.如果您希望从根组件运行更改检测(即,运行完整的更改检测检查),请ApplicationRef.tick()改为使用.(我ApplicationRef.tick()在plunker的评论中打了个电话.)注意调用tick()会导致ngDoCheck()被调用.
0xc*_*aff 128
如果您不想处理@angular/forms,可以使用Subject带有更改绑定的RxJS .
<input [ngModel]='model' (ngModelChange)='changed($event)' />
Run Code Online (Sandbox Code Playgroud)
import { Subject } from 'rxjs/Subject';
import { Component } from '@angular/core';
import 'rxjs/add/operator/debounceTime';
export class ViewComponent {
model: string;
modelChanged: Subject<string> = new Subject<string>();
constructor() {
this.modelChanged
.debounceTime(300) // wait 300ms after the last event before emitting last event
.distinctUntilChanged() // only emit if value is different from previous value
.subscribe(model => this.model = model);
}
changed(text: string) {
this.modelChanged.next(text);
}
}
Run Code Online (Sandbox Code Playgroud)
这确实触发了变化检测.对于不触发变化检测的方法,请查看Mark的答案.
.pipe(debounceTime(300), distinctUntilChanged()) rxjs需要6.
例:
constructor() {
this.modelChanged.pipe(
debounceTime(300),
distinctUntilChanged())
.subscribe(model => this.model = model);
}
Run Code Online (Sandbox Code Playgroud)
Ole*_*zky 33
它可以作为指令实施
import { Directive, Input, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core';
import { NgControl } from '@angular/forms';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import { Subscription } from 'rxjs';
@Directive({
selector: '[ngModel][onDebounce]',
})
export class DebounceDirective implements OnInit, OnDestroy {
@Output()
public onDebounce = new EventEmitter<any>();
@Input('debounce')
public debounceTime: number = 300;
private isFirstChange: boolean = true;
private subscription: Subscription;
constructor(public model: NgControl) {
}
ngOnInit() {
this.subscription =
this.model.valueChanges
.debounceTime(this.debounceTime)
.distinctUntilChanged()
.subscribe(modelValue => {
if (this.isFirstChange) {
this.isFirstChange = false;
} else {
this.onDebounce.emit(modelValue);
}
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
Run Code Online (Sandbox Code Playgroud)
用它就像
<input [(ngModel)]="value" (onDebounce)="doSomethingWhenModelIsChanged($event)">
Run Code Online (Sandbox Code Playgroud)
组件样本
import { Component } from "@angular/core";
@Component({
selector: 'app-sample',
template: `
<input[(ngModel)]="value" (onDebounce)="doSomethingWhenModelIsChanged($event)">
<input[(ngModel)]="value" (onDebounce)="asyncDoSomethingWhenModelIsChanged($event)">
`
})
export class SampleComponent {
value: string;
doSomethingWhenModelIsChanged(value: string): void {
console.log({ value });
}
async asyncDoSomethingWhenModelIsChanged(value: string): Promise<void> {
return new Promise<void>(resolve => {
setTimeout(() => {
console.log('async', { value });
resolve();
}, 1000);
});
}
}
Run Code Online (Sandbox Code Playgroud)
ber*_*ndg 28
不能像angular1那样直接访问,但你可以轻松使用NgFormControl和RxJS observables:
<input type="text" [ngFormControl]="term"/>
this.items = this.term.valueChanges
.debounceTime(400)
.distinctUntilChanged()
.switchMap(term => this.wikipediaService.search(term));
Run Code Online (Sandbox Code Playgroud)
这篇博文清楚地解释了它:http: //blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html
这是一个自动完成,但它适用于所有场景.
Jus*_*dow 23
由于主题较旧,因此大多数答案都不适用于Angular 6/7/8。
因此,这是使用RxJS的Angular 6+的简短解决方案。
首先导入必要的内容:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
Run Code Online (Sandbox Code Playgroud)
初始化ngOnInit:
export class MyComponent implements OnInit, OnDestroy {
public notesText: string;
private notesModelChanged: Subject<string> = new Subject<string>();
private notesModelChangeSubscription: Subscription
constructor() { }
ngOnInit() {
this.notesModelChangeSubscription = this.notesModelChanged
.pipe(
debounceTime(2000),
distinctUntilChanged()
)
.subscribe(newText => {
this.notesText = newText;
console.log(newText);
});
}
ngOnDestroy() {
this.notesModelChangeSubscription.unsubscribe();
}
}
Run Code Online (Sandbox Code Playgroud)
使用这种方式:
<input [ngModel]='notesText' (ngModelChange)='notesModelChanged.next($event)' />
Run Code Online (Sandbox Code Playgroud)
PS:对于更复杂,更有效的解决方案,您可能仍然需要检查其他答案。
Mat*_*ias 18
您可以创建一个RxJS(v.6)Observable,它可以执行您喜欢的任何操作.
<input type="text" (input)="onSearchChange($event.target.value)" />
Run Code Online (Sandbox Code Playgroud)
import { Observable } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
export class ViewComponent {
searchChangeObserver;
onSearchChange(searchValue: string) {
if (!this.searchChangeObserver) {
Observable.create(observer => {
this.searchChangeObserver = observer;
}).pipe(debounceTime(300)) // wait 300ms after the last event before emitting last event
.pipe(distinctUntilChanged()) // only emit if value is different from previous value
.subscribe(console.log);
}
this.searchChangeObserver.next(searchValue);
}
}
Run Code Online (Sandbox Code Playgroud)
Bra*_*d C 11
对于使用lodash人来说,这是非常容易抖任何功能:
changed = _.debounce(function() {
console.log("name changed!");
}, 400);
Run Code Online (Sandbox Code Playgroud)
然后把这样的东西扔到你的模板中:
<(input)="changed($event.target.value)" />
Run Code Online (Sandbox Code Playgroud)
直接在事件函数中初始化订阅者的解决方案:
import {Subject} from 'rxjs';
import {debounceTime, distinctUntilChanged} from 'rxjs/operators';
class MyAppComponent {
searchTermChanged: Subject<string> = new Subject<string>();
constructor() {
}
onFind(event: any) {
if (this.searchTermChanged.observers.length === 0) {
this.searchTermChanged.pipe(debounceTime(1000), distinctUntilChanged())
.subscribe(term => {
// your code here
console.log(term);
});
}
this.searchTermChanged.next(event);
}
}
Run Code Online (Sandbox Code Playgroud)
和 html:
<input type="text" (input)="onFind($event.target.value)">
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
139834 次 |
| 最近记录: |