Góc cạnh và ra mắt


160

Trong AngularJS tôi có thể gỡ bỏ một mô hình bằng cách sử dụng các tùy chọn ng-model.

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

Làm thế nào tôi có thể ra mắt một mô hình trong Angular? Tôi đã cố gắng tìm kiếm để gỡ lỗi trong các tài liệu nhưng tôi không thể tìm thấy bất cứ điều gì.

https://angular.io/search/#stq=debounce&stp=1

Một giải pháp sẽ là viết hàm gỡ lỗi của riêng tôi, ví dụ:

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

Và html của tôi

<input type=text [value]="firstName" #first (keyup)="firstNameChanged($event, first)">

Nhưng tôi đang tìm kiếm một chức năng xây dựng, có một trong Angular không?


3
Điều này có thể có liên quan github.com/angular/angular/issues/1773 , chưa được ngụ ý rõ ràng.
Eric Martinez

Kiểm tra bài này cho góc 7 với RxJS v6 freakyjolly.com/...
Mã Spy

Câu trả lời:


202

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 valueChangescó 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.


2
@Mark Rajcok Tôi nghĩ thay vì [value], bạn nên sử dụng [ngModel], vì [value] không cập nhật giá trị đầu vào.
Milad

1
Có phương pháp gỡ lỗi chung nào (ví dụ để áp dụng trên sự kiện thay đổi kích thước cửa sổ) không?
albanx

1
@MarkRajcok Tôi tin rằng vấn đề về CD mà bạn mô tả trong câu trả lời của bạn đã được giải quyết bởi github.com/angular/zone.js/pull/843
Jefftopia

2
Khi nào chúng ta cần hủy đăng ký để tránh rò rỉ bộ nhớ?
vu khống

1
@slanden Có, theo netbasal.com/when-to-unsubscribe-in-angular-d61c6b21bad3 , chúng tôi nên hủy đăng ký từ .fromEvent()đăng ký
Jon Onstott

153

Nếu bạn không muốn giải quyết @angular/forms, bạn chỉ có thể sử dụng RxJS Subjectvới các ràng buộc thay đổi.

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

Điều này không kích hoạt phát hiện thay đổi. Để biết cách không kích hoạt phát hiện thay đổi, hãy xem câu trả lời của Mark.


Cập nhật

.pipe(debounceTime(300), distinctUntilChanged()) là cần thiết cho rxjs 6.

Thí dụ:

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

5
Tôi thích giải pháp này! Làm việc với 2.0.0 góc, rxjs 5.0.0-beta 12
alsco77

2
Làm việc hoàn hảo, đơn giản và rõ ràng, không có hình thức liên quan. Tôi đang ở Angular 4.1.3, rxjs 5.1.1
thứ năm

Tôi nghĩ rằng đây là giải pháp ưu việt vì nó có tùy chọn để làm việc với các biểu mẫu nếu cần, nhưng loại bỏ sự phụ thuộc đó làm cho việc thực hiện đơn giản hơn nhiều. Cảm ơn.
Tối đa

2
.pipe(debounceTime(300), distinctUntilChanged())là cần thiết cho rxjs 6
Icycool 14/11/18

Giải pháp đã cứu tôi. Tôi đang sử dụng keyUpsự kiện input.nativeElementtrong một mat-table, nó đã ngừng hoạt động khi số lượng cột được thay đổi
igorepst 27/12/18

35

Nó có thể được thực hiện như là Chỉ thị

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

}

sử dụng nó như

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

mẫu thành phần

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
với nhiều lần nhập hơn, điều đó hiệu quả với tôi: nhập "rxjs / add / toán tử / debounceTime"; nhập "rxjs / add / toán tử / differUntilChanged";
Sbl

2
Điều này cho đến nay làm cho nó đơn giản nhất để thực hiện rộng ứng dụng
joshcomley

1
isFirstChange được sử dụng để không phát ra khi khởi tạo
Oleg Polarzky

2
Hoạt động trong Angular 8 và rxjs 6.5.2 với các thay đổi sau. Nếu bạn muốn sử dụng cú pháp ống, thay đổi như sau: import 'rxjs/add/operator/debounceTime'; import 'rxjs/add/operator/distinctUntilChanged';thành import { debounceTime, distinctUntilChanged } from 'rxjs/operators';this.model.valueChanges .debounceTime(this.debounceTime) .distinctUntilChanged()thànhthis.model.valueChanges .pipe( debounceTime(this.debounceTime), distinctUntilChanged() )
kumaheiyama

1
Hoạt động trong Angular 9 và rxjs 6.5.4 với những thay đổi @kumaheiyama đã nêu trong nhận xét của mình. Chỉ cần đừng quên xuất lệnh trong mô-đun nơi bạn đang tạo nó. Và đừng quên đưa mô-đun bạn đang tạo chỉ thị này vào mô-đun nơi bạn đang sử dụng mô-đun.
Filip Savic

29

Vì chủ đề đã cũ, hầu hết các câu trả lời không hoạt động trên Angular 6/7/8/9 và / hoặc sử dụng các lib khác.
Vì vậy, đây là một giải pháp ngắn và đơn giản cho Angular 6+ với RxJS.

Nhập những thứ cần thiết trước:

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

Khởi tạo vào 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();
  }
}

Sử dụng theo cách này:

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

PS: Đối với các giải pháp phức tạp và hiệu quả hơn, bạn vẫn có thể muốn kiểm tra các câu trả lời khác.


1
Tại sao bạn không hủy đăng ký này khi phá hủy?
Virendra Singh Rathore

Cập nhật. Cảm ơn đã chú ý!
Chỉ cần bóng tối

1
@JustShadow Cảm ơn bạn! Nó thực sự hữu ích.
Nirus Munjariya

Điều này hoạt động hoàn hảo trong lần thử đầu tiên. Nhưng khi tôi xóa văn bản tìm kiếm bằng cách nào đó, yêu cầu tiếp theo mất quá nhiều thời gian để đáp ứng.
Sadiksha Gautam

Điều đó thật lạ. Nó vẫn hoạt động tốt về phía tôi. Bạn có thể vui lòng chia sẻ thêm thông tin hoặc có thể mở một câu hỏi mới cho điều đó?
Chỉ cần bóng tối

28

Không thể truy cập trực tiếp như trong angular1 nhưng bạn có thể dễ dàng chơi với các đài quan sát NgFormControl và RxJS:

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

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

Bài đăng trên blog này giải thích rõ ràng: http : // blog. Dùtram.io / angan / 2016/06/06 / takes-nhược điểm-of-observables-in-angular2.html

Ở đây nó là cho một tự động hoàn thành nhưng nó hoạt động tất cả các kịch bản.


nhưng có lỗi từ dịch vụ, điều này không chạy lại
Arun Tyagi

Tôi không hiểu ví dụ. [...] là ràng buộc mục tiêu một chiều. Tại sao container có thể được thông báo valueChanges? không nên nó phải là sth. như thế (ngFormControl)="..."nào
phil294

20

Bạn có thể tạo RxJS (v.6) Có thể quan sát mà làm bất cứ điều gì bạn thích.

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


}

Cảm ơn đã giúp, tuy nhiên tôi nghĩ rằng việc nhập nên từ rsjs/Rx, tôi đã gặp lỗi khi sử dụng cách nhập như bạn đã viết ... vì vậy trong trường hợp của tôi bây giờ:import { Observable } from 'rxjs/Rx';
ghiscoding

2
@ghiscoding Nó phụ thuộc vào phiên bản rxjs. Trong phiên bản 6, nó là : import { Observable } from 'rxjs';.
Matthias

Cảm ơn! Như một bên, bạn chỉ có thể sử dụng một pipecuộc gọipipe(debounceTime(300), distinctUntilChanged())
al.

1
searchChangeObserver là một thuê bao, vì vậy searchChangeSubscacker sẽ là một cái tên tốt hơn.
Khonsort

12

Đối với bất cứ ai sử dụng lodash, nó là vô cùng dễ dàng để debounce bất kỳ chức năng:

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

sau đó chỉ cần ném một cái gì đó như thế này vào mẫu của bạn:

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

3
hoặc chỉ (đầu vào) = "đã thay đổi ($ event.target.value)"
Jamie Kudla

1
Cảm ơn bạn đã trả lời bằng lodash :)
Vamsi

Tôi tin rằng điều này vẫn sẽ kích hoạt phát hiện thay đổi Angular trên mỗi thay đổi, bất kể việc tranh luận.
AsoodAsItGets

5

Giải pháp với thuê bao khởi tạo trực tiếp trong chức năng sự kiện:

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

Và html:

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

Hoạt động hoàn toàn tốt cho hộp văn bản tự động hoàn thành 8 góc ng. Cảm ơn rất nhiều.
Jasmin Akther Suma

4

Tôi đã giải quyết điều này bằng cách viết một trang trí gỡ lỗi. Vấn đề được mô tả có thể được giải quyết bằng cách áp dụng @debounceAccessor cho bộ truy cập đã đặt của thuộc tính.

Tôi cũng đã cung cấp một trình trang trí gỡ lỗi bổ sung cho các phương thức, có thể hữu ích cho các dịp khác.

Điều này làm cho nó rất dễ dàng để công bố một tài sản hoặc một phương thức. Tham số là số mili giây mà việc gỡ lỗi sẽ kéo dài, 100 ms trong ví dụ dưới đây.

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


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

Và đây là mã cho các nhà trang trí:

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

Tôi đã thêm một tham số bổ sung cho trình trang trí phương thức, cho phép bạn kích hoạt phương thức SAU độ trễ gỡ lỗi. Tôi đã làm điều đó để tôi có thể sử dụng nó khi kết hợp với di chuột hoặc thay đổi kích thước các sự kiện, trong đó tôi muốn việc chụp xảy ra ở cuối luồng sự kiện. Tuy nhiên, trong trường hợp này, phương thức sẽ không trả về giá trị.


3

Chúng ta có thể tạo một lệnh [gỡ lỗi] ghi đè hàm viewToModelUpdate mặc định của ngModel bằng một lệnh trống.

Mã chỉ thị

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

Làm thế nào để sử dụng nó

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

2

Tệp HTML:

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

Tệp TS:

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

2

Giải pháp đơn giản là tạo ra một lệnh mà bạn có thể áp dụng cho bất kỳ điều khiển nào.

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

sử dụng sẽ là

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

Lớp này là xa biên dịch trong Angular 7.
Stephane

1

Đã dành hàng giờ cho việc này, hy vọng tôi có thể tiết kiệm thời gian cho người khác. Đối với tôi cách tiếp cận sau đây để sử dụng debouncetrên điều khiển là trực quan hơn và dễ hiểu hơn đối với tôi. Nó được xây dựng trên giải pháp tài liệu angular.io để tự động hoàn thành nhưng với khả năng tôi có thể chặn các cuộc gọi mà không phải phụ thuộc vào việc buộc dữ liệu vào DOM.

Plunker

Kịch bản ca sử dụng cho trường hợp này có thể là kiểm tra tên người dùng sau khi được nhập để xem ai đó đã lấy nó chưa, sau đó cảnh báo người dùng.

Lưu ý: đừng quên, (blur)="function(something.value)có thể có ý nghĩa hơn đối với bạn tùy thuộc vào nhu cầu của bạn.


1

Thời gian gỡ lỗi trong Angular 7 với RxJS v6

Liên kết nguồn

Liên kết demo

nhập mô tả hình ảnh ở đây

Trong mẫu HTML

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

Trong thành phần

    ....
    ....
    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

Bạn cũng có thể giải quyết điều này bằng cách sử dụng một trình trang trí, Ví dụ bằng cách sử dụng trình trang trí gỡ lỗi từ utils-decorator lib ( npm install utils-decorators):

import {debounce} from 'utils-decorators';

class MyAppComponent {

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

0

Đây là giải pháp tốt nhất mà tôi đã tìm thấy cho đến bây giờ. Cập nhật ngModeltrên blurdebounce

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

như mượn từ https://stackoverflow.com/a/47823960/3955513

Sau đó, trong HTML:

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

Trên blurmô hình được cập nhật rõ ràng bằng cách sử dụng javascript đơn giản.

Ví dụ ở đây: https://stackblitz.com/edit/ng2-debounce-usiness

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.