【问题标题】:How to achieve a debounce service on input keyup event in angular2 with rxjs如何使用rxjs在angular2中的输入keyup事件上实现去抖服务
【发布时间】:2017-06-15 13:57:50
【问题描述】:

我正在尝试在输入按键事件时调用服务。

HTML

<input placeholder="enter name" (keyup)='onKeyUp($event)'>

下面是onKeyUp()函数

onKeyUp(event) {
    let observable = Observable.fromEvent(event.target, 'keyup')
        .map(value => event.target.value)
        .debounceTime(1000)
        .distinctUntilChanged()
        .flatMap((search) => {
            // call the service
        });
    observable.subscribe((data) => {
        // data
    });
}

从浏览器的网络选项卡中发现,它在每个按键事件上调用按键功能(正如它应该做的那样),但我想要实现的是去抖时间每次服务调用之间间隔 1 秒。此外,如果我移动箭头键移动,则会触发该事件。

plunkr link

【问题讨论】:

  • 指定 RxJS 版本,因为版本 4 和 5 有很多不同
  • 这肯定是 RxJS 5,因为他使用的是 Angular2。
  • 您现在拥有的是正确的,所以您只是不知道如何调用服务或什么?
  • @martin 我可以调用该服务。但我面临的问题是对服务器的调用过多。在每个按键事件中,都会产生去抖效果。但是在去抖动时间之后,该服务被多次调用。也就是说,如果在去抖动时间之间有 10 个按键事件,则调用该服务 10 次。此外,如果我使用相同的搜索词移动左右箭头键,则会调用该服务(这是不希望的)。
  • @DDRamone "rxjs": "5.0.0-beta.12"

标签: javascript angular typescript rxjs rxjs5


【解决方案1】:

所以链确实是正确的,但问题是您正在创建一个 Observable 并在每个 keyup 事件上订阅它。这就是为什么它多次打印相同的值。只是有多个订阅,这不是您想要做的。

显然还有更多正确的方法,例如:

@Component({
  selector: 'my-app',
  template: `
    <div>
      <input type="text" (keyup)='keyUp.next($event)'>
    </div>
  `,
})
export class App implements OnDestroy {

  public keyUp = new Subject<KeyboardEvent>();

  private subscription: Subscription;

  constructor() {
    this.subscription = this.keyUp.pipe(
      map(event => event.target.value),
      debounceTime(1000),
      distinctUntilChanged(),
      mergeMap(search => of(search).pipe(
        delay(500),
      )),
    ).subscribe(console.log);
  }

  ngOnDestroy(): void {
    this.subscription.unsubscribe();
  }
}

查看更新后的演示:http://plnkr.co/edit/mAMlgycTcvrYf7509DOP

2019 年 1 月:针对 RxJS 6 更新

【讨论】:

  • 这可行,但有点误导。 keyUp 不是 Subject 类型。如果你想要 Subject 你必须在输入中指定 (keyup)='keyUp.next($event.target.value)'
  • 如果用户点击“Enter”,您希望执行什么逻辑而不等待谴责。有没有办法忽略谴责?
  • @BlackEagle 只需为 keyup.enter 事件添加与模板中相同的功能,例如 (keyup.enter)="console.log($event.target.value)"
【解决方案2】:

@marlin 提供了一个很好的解决方案,它在 angular 2.x 中运行良好,但在 angular 6 中,他们已经开始使用 rxjs 6.0 版本,并且语法略有不同,所以这里是更新的解决方案。

import {Component} from '@angular/core';
import {Observable, of, Subject} from 'rxjs';
import {debounceTime, delay, distinctUntilChanged, flatMap, map, tap} from 'rxjs/operators';

@Component({
    selector: 'my-app',
    template: `
        <div>
            <input type="text" (keyup)='keyUp.next($event)'>
        </div>
     `,
})
export class AppComponent {
    name: string;

    public keyUp = new Subject<string>();

    constructor() {
        const subscription = this.keyUp.pipe(
            map(event => event.target.value),
            debounceTime(1000),
            distinctUntilChanged(),
            flatMap(search => of(search).pipe(delay(500)))
        ).subscribe(console.log);
    }
}

【讨论】:

  • 错误 TS2339:“字符串”类型上不存在属性“目标”。
  • @FrancescoBorzì 您可以使用map((event: any) =&gt; (&lt;HTMLInputElement&gt;event.target).value), 或设置活动类型
  • 为什么会有额外的延迟(500)?事件不是已经被 1000 的bounceTime 延迟了吗?
【解决方案3】:

嗯,这是一个基本的去抖动器。

ngOnInit ( ) {
        let inputBox = this.myInput.nativeElement;
        let displayDiv = this.myDisplay.nativeElement;

        let source = Rx.Observable.fromEvent ( inputBox, 'keyup' )
            .map ( ( x ) => { return x.currentTarget.value; } )
            .debounce ( ( x ) => { return Rx.Observable.timer ( 1000 ); } );

        source.subscribe (
            ( x ) => { displayDiv.innerText = x; },
            ( err ) => { console.error ( 'Error: %s', err ) },
            () => {} );
    }
}

如果您设置了两个指示的子视图(输入和显示),您将看到去抖动工作。不确定这是否对您没有任何作用,但这种基本形式(据我所知)是去抖动的直接方法,我经常使用这个起点,内部文本的集合只是一个示例,它可以拨打服务电话或您需要它做的任何其他事情。

【讨论】:

    【解决方案4】:

    我建议您查看 Angular2 教程,该教程显示了一个与您所要求的内容类似的干净且解释的示例。

    https://angular.io/docs/ts/latest/tutorial/toh-pt6.html#!#observables

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-08
      • 2021-03-07
      • 1970-01-01
      • 2017-08-26
      • 2021-11-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多