เชิงมุมและ debounce


160

ใน AngularJS ฉันสามารถลบล้างโมเดลโดยใช้ตัวเลือก ng-model

ng-model-options="{ debounce: 1000 }"

ฉันจะลบล้างโมเดลใน Angular ได้อย่างไร ฉันพยายามค้นหา debounce ในเอกสาร แต่ไม่พบอะไรเลย

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)">

แต่ฉันกำลังมองหาฟังก์ชั่นการสร้างมีหนึ่งในแองกูลาร์หรือไม่?


3
นี่อาจเป็นgithub.com/angular/angular/issues/1773 ที่เกี่ยวข้องซึ่งยังไม่ได้ใส่เข้าไป
Eric Martinez

ตรวจสอบการโพสต์นี้สำหรับเชิงมุม 7 กับ RxJS v6 freakyjolly.com/...
Spy รหัส

คำตอบ:


202

อัปเดตสำหรับ RC.5

ด้วยเชิงมุม 2 เราสามารถใช้ประกอบการ debounce RxJS 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

รหัสด้านบนนี้ยังมีตัวอย่างของวิธีการปรับขนาดหน้าต่างเค้นกิจกรรมตามที่ @albanx ถามในความคิดเห็นด้านล่าง


แม้ว่ารหัสข้างต้นอาจเป็นวิธีเชิงมุมของการทำมันก็ไม่ได้มีประสิทธิภาพ การกดแป้นทุกครั้งและทุกเหตุการณ์ที่ปรับขนาดแม้ว่าจะถูกคัดออกและควบคุมปริมาณ แต่ส่งผลให้การตรวจจับการเปลี่ยนแปลงทำงานอยู่ ในคำอื่น ๆdebouncing และการควบคุมปริมาณไม่ส่งผลกระทบต่อวิธีการที่มักจะเปลี่ยนการตรวจสอบการทำงาน (ฉันพบความคิดเห็น GitHubโดย Tobias Bosch ที่ยืนยันสิ่งนี้) คุณสามารถเห็นสิ่งนี้เมื่อคุณเรียกใช้ตัวเรียงลำดับและคุณเห็นจำนวนครั้งที่ngDoCheck()ถูกเรียกเมื่อคุณพิมพ์ลงในกล่องอินพุตหรือปรับขนาดหน้าต่าง (ใช้ปุ่ม "x" สีน้ำเงินเพื่อเรียกใช้งานพลั่วเกอร์ในหน้าต่างแยกต่างหากเพื่อดูเหตุการณ์การปรับขนาด)

เทคนิคที่มีประสิทธิภาพมากขึ้นคือการสร้าง RxJS สังเกตได้จากเหตุการณ์นอก "โซน" ของ 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

ฉันใช้ngAfterViewInit()แทนngOnInit()เพื่อให้แน่ใจว่าinputElRefมีการกำหนดไว้

detectChanges()จะทำงานการตรวจสอบการเปลี่ยนแปลงในส่วนนี้และเด็ก หากคุณต้องการเรียกใช้การตรวจจับการเปลี่ยนแปลงจากองค์ประกอบหลัก (เช่นให้เรียกใช้การตรวจสอบการเปลี่ยนแปลงแบบเต็ม) จากนั้นใช้ApplicationRef.tick()แทน (ฉันใส่การเรียกไปยังApplicationRef.tick()ข้อคิดเห็นใน plunker) โปรดทราบว่าการโทรtick()จะทำให้ngDoCheck()ถูกเรียก


2
@ Mark Rajcok ผมคิดแทน [value] คุณควรใช้ [ngModel] เพราะ [ค่า] ไม่ปรับปรุงค่าการป้อนข้อมูล
Milad

1
มีวิธี debounce ทั่วไป (ตัวอย่างเช่นเพื่อใช้ในเหตุการณ์การปรับขนาดหน้าต่าง)?
albanx

1
@MarkRajcok ฉันเชื่อว่าปัญหาซีดีที่คุณอธิบายไว้ในคำตอบของคุณได้รับการแก้ไขโดยgithub.com/angular/zone.js/pull/843
Jefftopia

2
เมื่อใดที่เราจะต้องยกเลิกการสมัครเพื่อป้องกันการรั่วไหลของหน่วยความจำ
ใส่ร้าย

1
@landland ใช่ตามnetbasal.com/when-to-unsubscribe-in-angular-d61c6b21bad3เราควรยกเลิกการ.fromEvent()สมัครรับข้อมูล
Jon Onstott

153

หากคุณไม่ต้องการจัดการ@angular/formsคุณสามารถใช้ RxJS Subjectกับการผูกการเปลี่ยนแปลงได้

view.component.html

<input [ngModel]='model' (ngModelChange)='changed($event)' />

view.component.ts

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);
    }
}

นี่เป็นการตรวจจับการเปลี่ยนแปลงทริกเกอร์ สำหรับวิธีที่ไม่ก่อให้เกิดการตรวจจับการเปลี่ยนแปลงให้ตรวจสอบคำตอบของ Mark


ปรับปรุง

.pipe(debounceTime(300), distinctUntilChanged()) จำเป็นสำหรับ rxjs 6

ตัวอย่าง:

   constructor() {
        this.modelChanged.pipe(
            debounceTime(300), 
            distinctUntilChanged())
            .subscribe(model => this.model = model);
    }

5
ฉันชอบวิธีนี้! ทำงานร่วมกับเชิงมุม 2.0.0 rxjs 5.0.0 เบต้า 12
alsco77

2
ทำงานอย่างสมบูรณ์แบบเรียบง่ายและชัดเจนไม่มีรูปแบบที่เกี่ยวข้อง ฉันใช้แองกูลาร์ 4.1.3, rxjs 5.1.1
ห้า

ฉันคิดว่านี่เป็นโซลูชั่นที่เหนือกว่าเนื่องจากมีตัวเลือกในการทำงานกับแบบฟอร์มหากจำเป็น แต่ลบการพึ่งพาที่ทำให้การใช้งานที่ง่ายขึ้นมาก ขอบคุณ
Max

2
.pipe(debounceTime(300), distinctUntilChanged())เป็นสิ่งจำเป็นสำหรับ rxjs 6
Icycool

วิธีแก้ปัญหาช่วยฉันได้ ผมใช้keyUpเหตุการณ์input.nativeElementในmat-tableที่หยุดทำงานเมื่อจำนวนของคอลัมน์ก็เปลี่ยน
igorepst

35

มันสามารถนำมาใช้เป็นคำสั่ง

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();
  }

}

ใช้มันเหมือน

<input [(ngModel)]="value" (onDebounce)="doSomethingWhenModelIsChanged($event)">

ตัวอย่างส่วนประกอบ

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);
    });
  }
} 

1
ด้วยการนำเข้าเพิ่มเติมที่ทำงานกับฉัน: นำเข้า "rxjs / เพิ่ม / ผู้ประกอบการ / debounceTime"; นำเข้า "rxjs / add / operator / differentUntilChanged";
SBL

2
นี้โดยไกลทำให้มันง่ายที่จะใช้การประยุกต์กว้าง
joshcomley

1
isFirstChange ใช้เพื่อไม่ให้ปล่อยออกมาในการเริ่มต้น
Oleg Polezky

2
ทำงานใน Angular 8 และ rxjs 6.5.2 ด้วยการเปลี่ยนแปลงต่อไปนี้ หากคุณต้องการใช้ไวยากรณ์ของ pipe ให้เปลี่ยนสิ่งต่อไปนี้: import 'rxjs/add/operator/debounceTime'; import 'rxjs/add/operator/distinctUntilChanged';เป็นimport { debounceTime, distinctUntilChanged } from 'rxjs/operators';และthis.model.valueChanges .debounceTime(this.debounceTime) .distinctUntilChanged()ไปthis.model.valueChanges .pipe( debounceTime(this.debounceTime), distinctUntilChanged() )
kumaheiyama

1
ทำงานใน Angular 9 และ rxjs 6.5.4 พร้อมกับการเปลี่ยนแปลง @kumaheiyama ที่ระบุไว้ในความคิดเห็นของเขา ก็อย่าลืมที่จะส่งออกคำสั่งในโมดูลที่คุณกำลังสร้างมัน และไม่ลืมที่จะรวมโมดูลที่คุณกำลังสร้างคำสั่งนี้ในลงในโมดูลที่คุณจะใช้มัน
Filip Savic

29

เนื่องจากหัวข้อเก่าคำตอบส่วนใหญ่จะไม่ทำงานกับAngular 6/7/8/9และ / หรือใช้ libs อื่น ๆ
ดังนั้นนี่คือคำตอบสั้น ๆ และง่ายสำหรับ Angular 6+ กับ RxJS

นำเข้าสิ่งที่จำเป็นก่อน:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';

เริ่มต้นเมื่อ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();
  }
}

ใช้วิธีนี้:

<input [ngModel]='notesText' (ngModelChange)='notesModelChanged.next($event)' />

PS: สำหรับโซลูชันที่ซับซ้อนและมีประสิทธิภาพยิ่งขึ้นคุณอาจต้องการตรวจสอบคำตอบอื่น ๆ


1
ทำไมคุณไม่ยกเลิกการเป็นสมาชิกนี้เมื่อทำลาย?
Virendra Singh Rathore

Updated ขอบคุณที่สังเกต!
เพียงแค่เงา

1
@ JustShadow ขอขอบคุณ! มันมีประโยชน์จริงๆ
Niral Munjariya

การทำงานนี้สมบูรณ์แบบในการลองครั้งแรก แต่เมื่อฉันลบข้อความที่ค้นหาอย่างใดคำขอต่อไปใช้เวลานานเกินไปในการตอบสนอง
Sadiksha Gautam

มันแปลกมาก มันยังทำงานได้ดีในด้านของฉัน คุณช่วยแบ่งปันข้อมูลเพิ่มเติมหรือเปิดคำถามใหม่ได้ไหม?
เพียงเงา

28

ไม่สามารถเข้าถึงได้โดยตรงเช่นใน angular1 แต่คุณสามารถเล่นกับสิ่งที่สังเกตได้ NgFormControl และ RxJS ได้อย่างง่ายดาย:

<input type="text" [ngFormControl]="term"/>

this.items = this.term.valueChanges
  .debounceTime(400)
  .distinctUntilChanged()
  .switchMap(term => this.wikipediaService.search(term));

โพสต์บล็อกนี้อธิบายไว้อย่างชัดเจน: http : // blog. Thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html

ที่นี่มีไว้สำหรับการเติมข้อความอัตโนมัติ แต่ใช้งานได้กับทุกสถานการณ์


แต่มีข้อผิดพลาดจากการให้บริการนี้ไม่ได้ทำงานอีกครั้ง
อรุณ Tyagi

ฉันไม่เข้าใจตัวอย่าง [...] เป็นการเชื่อมโยงเป้าหมายทางเดียว ทำไมคอนเทนเนอร์ถึงได้รับการแจ้งเตือนvalueChanges? ไม่ควรจะต้องเป็น ชอบ(ngFormControl)="..."ไหม
phil294

20

คุณสามารถสร้าง RxJS (v.6) สังเกตได้ซึ่งทำสิ่งที่คุณต้องการ

view.component.html

<input type="text" (input)="onSearchChange($event.target.value)" />

view.component.ts

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);
  }  


}

ขอบคุณที่ช่วย แต่ฉันคิดว่าการนำเข้าควรมาจากrsjs/Rxฉันมีข้อผิดพลาดเมื่อใช้การนำเข้าในแบบที่คุณเขียน ... ดังนั้นในกรณีของฉันตอนนี้:import { Observable } from 'rxjs/Rx';
ghiscoding

2
@ghiscoding มันขึ้นอยู่กับรุ่น rxjs ในเวอร์ชัน 6 มันคือ: import { Observable } from 'rxjs';.
Matthias

ขอบคุณ! คุณสามารถใช้เพียงหนึ่งpipeสายpipe(debounceTime(300), distinctUntilChanged())
อัล

1
searchChangeObserver เป็นสมาชิกดังนั้น searchChangeSubscriber จะเป็นชื่อที่ดีกว่า
Khonsort

12

สำหรับทุกคนที่ใช้ lodash มันเป็นเรื่องง่ายอย่างยิ่งที่จะทำการดีบัคฟังก์ชั่นใด ๆ :

changed = _.debounce(function() {
    console.log("name changed!");
}, 400);

จากนั้นเพียงแค่โยนสิ่งนี้ลงในเทมเพลตของคุณ:

<(input)="changed($event.target.value)" />

3
หรือแค่ (อินพุต) = "เปลี่ยนแปลง ($ event.target.value)"
Jamie Kudla

1
ขอบคุณสำหรับการตอบด้วย lodash :)
Vamsi

ฉันเชื่อว่าสิ่งนี้จะยังคงเรียกใช้การตรวจจับการเปลี่ยนแปลงเชิงมุมในทุก ๆ การเปลี่ยนแปลงโดยไม่คำนึงถึงการเปิดตัว
AsGoodAsItGets เมื่อ

5

วิธีการแก้ปัญหาด้วยการสมัครสมาชิกเริ่มต้นโดยตรงในฟังก์ชั่นเหตุการณ์:

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);
    }
}

และ html:

<input type="text" (input)="onFind($event.target.value)">

ใช้งานได้ดีกับกล่องข้อความเติมข้อความอัตโนมัติ 8 มุมเชิงมุมสมบูรณ์ ขอบคุณมาก.
Jasmin Akther Suma

4

ฉันแก้ไขได้โดยเขียนมัณฑนากร debounce ปัญหาที่อธิบายสามารถแก้ไขได้โดยใช้ @debounceAccessor กับชุดการเข้าถึงของคุณสมบัติ

ฉันได้ให้ตัวตกแต่ง debounce เพิ่มเติมสำหรับวิธีการซึ่งจะเป็นประโยชน์สำหรับโอกาสอื่น ๆ

สิ่งนี้ทำให้การ debounce คุณสมบัติหรือเมธอดเป็นเรื่องง่าย พารามิเตอร์คือจำนวนมิลลิวินาทีที่ debounce ควรใช้อยู่คือ 100 ms ในตัวอย่างด้านล่าง

@debounceAccessor(100)
set myProperty(value) {
  this._myProperty = value;
}


@debounceMethod(100)
myMethod (a, b, c) {
  let d = a + b + c;
  return d;
}

และนี่คือรหัสสำหรับผู้ตกแต่ง:

function debounceMethod(ms: number, applyAfterDebounceDelay = false) {

  let timeoutId;

  return function (target: Object, propName: string, descriptor: TypedPropertyDescriptor<any>) {
    let originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
      if (timeoutId) return;
      timeoutId = window.setTimeout(() => {
        if (applyAfterDebounceDelay) {
          originalMethod.apply(this, args);
        }
        timeoutId = null;
      }, ms);

      if (!applyAfterDebounceDelay) {
        return originalMethod.apply(this, args);
      }
    }
  }
}

function debounceAccessor (ms: number) {

  let timeoutId;

  return function (target: Object, propName: string, descriptor: TypedPropertyDescriptor<any>) {
    let originalSetter = descriptor.set;
    descriptor.set = function (...args: any[]) {
      if (timeoutId) return;
      timeoutId = window.setTimeout(() => {
        timeoutId = null;
      }, ms);
      return originalSetter.apply(this, args);
    }
  }
}

ฉันเพิ่มพารามิเตอร์เพิ่มเติมสำหรับวิธีตกแต่งภายในซึ่งให้คุณเรียกใช้วิธีหลังจากการดีเลย์ดีเลย์ ฉันทำเช่นนั้นดังนั้นฉันสามารถใช้มันได้เมื่อเชื่อมต่อกับ mouseover หรือปรับขนาดเหตุการณ์ซึ่งฉันต้องการให้การดักจับเกิดขึ้นเมื่อสิ้นสุดสตรีมเหตุการณ์ ในกรณีนี้วิธีการจะไม่ส่งคืนค่า


3

เราสามารถสร้างคำสั่ง [debounce] ซึ่งเขียนทับฟังก์ชัน viewToModelUpdate ที่เป็นค่าเริ่มต้นของ ngModel ด้วยค่าว่าง

รหัส Directive

@Directive({ selector: '[debounce]' })
export class MyDebounce implements OnInit {
    @Input() delay: number = 300;

    constructor(private elementRef: ElementRef, private model: NgModel) {
    }

    ngOnInit(): void {
        const eventStream = Observable.fromEvent(this.elementRef.nativeElement, 'keyup')
            .map(() => {
                return this.model.value;
            })
            .debounceTime(this.delay);

        this.model.viewToModelUpdate = () => {};

        eventStream.subscribe(input => {
            this.model.viewModel = input;
            this.model.update.emit(input);
        });
    }
}

วิธีใช้งาน

<div class="ui input">
  <input debounce [delay]=500 [(ngModel)]="myData" type="text">
</div>

2

ไฟล์ HTML:

<input [ngModel]="filterValue"
       (ngModelChange)="filterValue = $event ; search($event)"
        placeholder="Search..."/>

ไฟล์ TS:

timer = null;
time = 250;
  search(searchStr : string) : void {
    clearTimeout(this.timer);
    this.timer = setTimeout(()=>{
      console.log(searchStr);
    }, time)
  }

2

วิธีง่ายๆคือการสร้างคำสั่งที่คุณสามารถนำไปใช้กับการควบคุมใด ๆ

import { Directive, ElementRef, Input, Renderer, HostListener, Output, EventEmitter } from '@angular/core';
import { NgControl } from '@angular/forms';

@Directive({
    selector: '[ngModel][debounce]',
})
export class Debounce 
{
    @Output() public onDebounce = new EventEmitter<any>();

    @Input('debounce') public debounceTime: number = 500;

    private modelValue = null;

    constructor(public model: NgControl, el: ElementRef, renderer: Renderer){
    }

    ngOnInit(){
        this.modelValue = this.model.value;

        if (!this.modelValue){
            var firstChangeSubs = this.model.valueChanges.subscribe(v =>{
                this.modelValue = v;
                firstChangeSubs.unsubscribe()
            });
        }

        this.model.valueChanges
            .debounceTime(this.debounceTime)
            .distinctUntilChanged()
            .subscribe(mv => {
                if (this.modelValue != mv){
                    this.modelValue = mv;
                    this.onDebounce.emit(mv);
                }
            });
    }
}

การใช้งานจะเป็น

<textarea [ngModel]="somevalue"   
          [debounce]="2000"
          (onDebounce)="somevalue = $event"                               
          rows="3">
</textarea>

Angular 7ชั้นนี้อยู่ไกลจากการรวบรวมใน
Stephane

1

ใช้เวลาหลายชั่วโมงกับสิ่งนี้หวังว่าฉันจะช่วยคนอื่นได้บ้างในบางเวลา สำหรับฉันวิธีการต่อไปนี้เพื่อใช้debounceในการควบคุมนั้นใช้งานง่ายและเข้าใจง่ายขึ้นสำหรับฉัน มันถูกสร้างขึ้นบน angular.io docs solution สำหรับการเติมข้อความอัตโนมัติ แต่ด้วยความสามารถในการสกัดกั้นการโทรโดยไม่ต้องพึ่งการผูกข้อมูลกับ DOM

Plunker

สถานการณ์กรณีใช้งานสำหรับสิ่งนี้อาจตรวจสอบชื่อผู้ใช้หลังจากที่พิมพ์เพื่อดูว่ามีใครบางคนทำไปแล้วจากนั้นเตือนผู้ใช้

หมายเหตุ: อย่าลืม(blur)="function(something.value)อาจทำให้คุณรู้สึกดีขึ้นตามความต้องการของคุณ


1

DebounceTime เป็น Angular 7 พร้อม RxJS v6

ลิงค์ที่มา

ลิงค์สาธิต

ป้อนคำอธิบายรูปภาพที่นี่

ในเทมเพลต HTML

<input type="text" #movieSearchInput class="form-control"
            placeholder="Type any movie name" [(ngModel)]="searchTermModel" />

ในองค์ประกอบ

    ....
    ....
    export class AppComponent implements OnInit {

    @ViewChild('movieSearchInput') movieSearchInput: ElementRef;
    apiResponse:any;
    isSearching:boolean;

        constructor(
        private httpClient: HttpClient
        ) {
        this.isSearching = false;
        this.apiResponse = [];
        }

    ngOnInit() {
        fromEvent(this.movieSearchInput.nativeElement, 'keyup').pipe(
        // get value
        map((event: any) => {
            return event.target.value;
        })
        // if character length greater then 2
        ,filter(res => res.length > 2)
        // Time in milliseconds between key events
        ,debounceTime(1000)        
        // If previous query is diffent from current   
        ,distinctUntilChanged()
        // subscription for response
        ).subscribe((text: string) => {
            this.isSearching = true;
            this.searchGetCall(text).subscribe((res)=>{
            console.log('res',res);
            this.isSearching = false;
            this.apiResponse = res;
            },(err)=>{
            this.isSearching = false;
            console.log('error',err);
            });
        });
    }

    searchGetCall(term: string) {
        if (term === '') {
        return of([]);
        }
        return this.httpClient.get('http://www.omdbapi.com/?s=' + term + '&apikey=' + APIKEY,{params: PARAMS.set('search', term)});
    }

    }

1

คุณสามารถแก้ปัญหานี้ได้โดยใช้มัณฑนากรตัวอย่างเช่นโดยใช้ debounce decorator จากutils-decorator lib ( npm install utils-decorators):

import {debounce} from 'utils-decorators';

class MyAppComponent {

  @debounce(500)
  firstNameChanged($event, first) {
   ...
  }
}

0

นี่เป็นทางออกที่ดีที่สุดที่ฉันพบมาจนถึงตอนนี้ อัพเดทการngModelเปิดblurและdebounce

import { Directive, Input, Output, EventEmitter,ElementRef } from '@angular/core';
import { NgControl, NgModel } from '@angular/forms';
import 'rxjs/add/operator/debounceTime'; 
import 'rxjs/add/operator/distinctUntilChanged';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/map';

@Directive({
    selector: '[ngModel][debounce]',
})
export class DebounceDirective {
    @Output()
    public onDebounce = new EventEmitter<any>();

    @Input('debounce')
    public debounceTime: number = 500;

    private isFirstChange: boolean = true;

    constructor(private elementRef: ElementRef, private model: NgModel) {
    }

    ngOnInit() {
        const eventStream = Observable.fromEvent(this.elementRef.nativeElement, 'keyup')
            .map(() => {
                return this.model.value;
            })
            .debounceTime(this.debounceTime);

        this.model.viewToModelUpdate = () => {};

        eventStream.subscribe(input => {
            this.model.viewModel = input;
            this.model.update.emit(input);
        });
    }
}

ตามที่ยืมมาจากhttps://stackoverflow.com/a/47823960/3955513

จากนั้นใน HTML:

<input [(ngModel)]="hero.name" 
        [debounce]="3000" 
        (blur)="hero.name = $event.target.value"
        (ngModelChange)="onChange()"
        placeholder="name">

เกี่ยวblurกับรูปแบบการปรับปรุงอย่างชัดเจนโดยใช้จาวาสคริปต์ธรรมดา

ตัวอย่างที่นี่: https://stackblitz.com/edit/ng2-debounce-working

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.