각도 및 디 바운스
AngularJS에서는 ng-model 옵션을 사용하여 모델을 디 바운스 할 수 있습니다.
ng-model-options="{ debounce: 1000 }"
Angular에서 모델을 어떻게 디 바운스 할 수 있습니까? 문서에서 디 바운스를 검색하려고했지만 아무것도 찾지 못했습니다.
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);
그리고 내 HTML
<input type=text [value]="firstName" #first (keyup)="firstNameChanged($event, first)">
그러나 함수 빌드를 찾고 있는데 Angular에 있습니까?
RC.5 용으로 업데이트
Angular 2를 사용 debounceTime()
하면 양식 컨트롤의 valueChanges
관찰 가능 항목 에서 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();
}
}
위의 코드에는 아래 주석에서 @albanx의 요청에 따라 창 크기 조정 이벤트를 조절하는 방법에 대한 예제도 포함되어 있습니다.
위의 코드는 아마도 Angular-way 방식이지만 효율적이지 않습니다. 모든 키 입력 및 크기 조정 이벤트는 디 바운스되고 조절 되더라도 변경 감지가 실행됩니다. 다시 말해, 디 바운싱 및 스로틀 링은 변경 감지 실행 빈도에 영향을 미치지 않습니다 . ( Tobias Bosch 의 GitHub 주석 에서이를 확인했습니다.) 플런저를 실행할 때이 내용을 볼 ngDoCheck()
수 있으며 입력 상자에 입력하거나 창 크기를 조정할 때 호출 횟수를 확인할 수 있습니다. 크기 조정 이벤트를 보려면 파란색 "x"버튼을 사용하여 플런저를 별도의 창에서 실행하십시오.
보다 효율적인 기술은 Angular의 "영역"외부의 이벤트에서 RxJS Observables를 직접 만드는 것입니다. 이렇게하면 이벤트가 발생할 때마다 변경 감지가 호출되지 않습니다. 그런 다음 구독 콜백 메소드에서 변경 감지를 수동으로 트리거하십시오. 즉, 변경 감지가 호출되는시기를 제어합니다.
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();
}
}
나는 그것을 정의 하기 위해 ngAfterViewInit()
대신 사용 합니다 .ngOnInit()
inputElRef
detectChanges()
이 구성 요소와 해당 하위 항목에서 변경 감지를 실행합니다. 루트 구성 요소에서 변경 감지를 실행하려면 (즉, 전체 변경 감지 확인을 실행) ApplicationRef.tick()
대신 사용하십시오. ( ApplicationRef.tick()
플 런커의 의견에 전화를 걸었습니다.) 전화 tick()
하면 전화 가 걸립니다 ngDoCheck()
.
을 다루지 않으려면 변경 바인딩과 함께 @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);
}
지시어로 구현 될 수 있습니다.
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);
});
}
}
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
자동 완성을위한 것이지만 모든 시나리오에서 작동합니다.
화제가 된 때문에, 답변의 대부분은 일을하지 않습니다 에 각도 6/7/8 .
RxJS가 포함 된 Angular 6+를위한 짧고 간단한 솔루션입니다.
필요한 것들을 먼저 가져 오십시오 :
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 {
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)' />
추신 : 더 복잡하고 효율적인 솔루션을 위해 여전히 다른 답변을 확인하고 싶을 수도 있습니다.
원하는 것을 수행 하는 RxJS (v.6) Observable 을 만들 수 있습니다 .
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);
}
}
lodash를 사용하는 사람이라면 어떤 기능이든 쉽게 디 바운싱 할 수 있습니다.
changed = _.debounce(function() {
console.log("name changed!");
}, 400);
그런 다음 템플릿에 다음과 같은 것을 던져 넣으십시오.
<(input)="changed($event.target.value)" />
디 바운스 데코레이터를 작성하여이 문제를 해결했습니다. @debounceAccessor를 속성의 set 접근 자에 적용하면 설명 된 문제를 해결할 수 있습니다.
또한 다른 경우에 유용 할 수있는 방법에 대한 추가 디 바운스 데코레이터를 제공했습니다.
이렇게하면 속성이나 메서드를 매우 쉽게 디 바운싱 할 수 있습니다. 이 매개 변수는 디 바운스가 지속되어야하는 시간 (밀리 초)이며 아래 예에서 100ms입니다.
@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);
}
}
}
디 바운스 지연 후 메소드를 트리거 할 수있는 메소드 데코레이터에 대한 추가 매개 변수를 추가했습니다. 예를 들어 마우스 오버 또는 크기 조정 이벤트와 결합 할 때 이벤트 스트림의 끝에서 캡처가 발생하기를 원할 때 사용할 수 있습니다. 그러나이 경우 메서드는 값을 반환하지 않습니다.
ngModel의 기본 viewToModelUpdate 함수를 빈 것으로 덮어 쓰는 [debounce] 지시어를 만들 수 있습니다.
지시어 코드
@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>
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)
}
간단한 해결책은 모든 컨트롤에 적용 할 수있는 지시문을 만드는 것입니다.
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>
이것에 시간을 보냈 으면 좋겠다. 다른 누군가를 구할 수 있기를 바랍니다. 나에게 debounce
컨트롤 에서 사용하는 다음과 같은 접근 방식 이 더 직관적이고 이해하기 쉽습니다. 자동 완성을 위해 angular.io 문서 솔루션을 기반으로하지만 DOM에 데이터를 연결하지 않고도 호출을 가로 챌 수 있습니다.
유스 케이스 시나리오는 사용자 이름을 입력 한 후 누군가가 이미 그것을 가지고 있는지 확인한 다음 사용자에게 경고하는 것일 수 있습니다.
참고 : 필요 (blur)="function(something.value)
에 따라 더 의미가있을 수 있습니다.
이벤트 기능에서 직접 초기화 가입자가있는 솔루션 :
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)">
RxJS v6을 사용하는 Angular 7의 DebounceTime
소스 링크
데모 링크
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)});
}
}
이것이 지금까지 내가 찾은 최고의 솔루션입니다. (가) 업데이트 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
참고 URL : https://stackoverflow.com/questions/32051273/angular-and-debounce
'IT story' 카테고리의 다른 글
Datetime-다음 화요일에 받기 (0) | 2020.06.20 |
---|---|
LDAP는 무엇에 사용됩니까? (0) | 2020.06.20 |
쿼리 문자열에 값 추가 (0) | 2020.06.20 |
$ .focus ()가 작동하지 않습니다 (0) | 2020.06.20 |
Github Wiki에서 JSON 블록의 스타일을 지정하는 방법은 무엇입니까? (0) | 2020.06.20 |