RXJS 处理两个不同的事件,其中第一次发生必须取消第二次 [英] RXJS to handle two different events, where the first occurance must cancel the second

查看:99
本文介绍了RXJS 处理两个不同的事件,其中第一次发生必须取消第二次的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

rxjs 对我来说非常具有挑战性,我发现自己一直在努力解决这个问题.我在堆栈上找到的最接近的解决方案是使用合并运算符.这是链接

rxjs is pretty challenging for me and I have found myself stuck trying to solve this problem. The closest solution I have found on stack is the usage of merge operator. Here is the link

我正在使用 angular 2.

I am working in angular 2.

我在 html 中有一个输入搜索字段

I have an input search field in html

<input (keydown.enter)="setFocus()" id="search-box" name="input-box" class="search-box" type="text" placeholder="Client search" (focus)="clearWarnings()" />

用户在字段框中输入,在预设的延迟后会触发相应的功能.用户也可以按下回车键(和搜索图标)来触发搜索.我在这里的目标是每当用户按下回车键时,去抖动不应触发搜索,因为它已经在运行.

The user types in the field box which will trigger the corresponding function after a preset delay. Also the user may press the enter key( and a search icon) to trigger the search. What I aim here is to whenever the user hits the enter key, the debounce should NOT trigger the search because it is already running.

这是到目前为止使用合并函数的代码,尽管它似乎没有按照我的预期工作.

Here is the code so far using the merge function although it does not seem to work the way I intend.

ngAfterViewInit() {
        this.currentMember = this.appHelpersService.getLocalStorageSearchedRim();
        if (this.currentMember) {
            this.searchService.changeSearchTerm(this.currentMember);
        }

        var waitTime = AppConstants.SEARCH_TEXT_WAITTIME;
        const searchSource = document.getElementById("search-box");
        const keydownStream = fromEvent(this.elementRef.nativeElement, 'keyup');
        const inputStream = fromEvent(searchSource, "input");
        const allStreams = merge(keydownStream, inputStream);
        allStreams
            .pipe(
                map((event: KeyboardEvent | MouseEvent) => (<HTMLInputElement>event.target).value.trim()),
                filter((searchTerm: string) => {
                    waitTime = Number(searchTerm) ? AppConstants.SEARCH_NUMERIC_WAITTIME : AppConstants.SEARCH_TEXT_WAITTIME;
                    return searchTerm.length >= 2;
                }),
                debounce(() => timer(waitTime)),
                distinctUntilChanged()
            )
            .subscribe((searchTerm: string) => {
                this.showProgressbar = true;
                this.listSearchResults(searchTerm);
            });

    }

还有回车键事件:

 setFocus(): void {
        const searchBox: HTMLElement = document.getElementById("search-box");
        const searchTerm = (<HTMLInputElement>searchBox).value;
        if (searchTerm && searchTerm.length > 0) {
            this.listSearchResults(searchTerm);
        }
        searchBox.focus();
    }

在我提到的解决方案中,所有合并在一起的事件将触发该函数,但不一定取消正在等待的其他事件(去抖动).

In the solution I have mentioned all events merged together will trigger the function but not necessarily cancel the other event that is waiting (debounce).

感谢您的时间

推荐答案

我认为您的代码片段中存在一些错误

I think you have some errors in your snippet

const keydownStream = fromEvent(this.elementRef.nativeElement, 'keyup');

应该是

const keyupStream = fromEvent(this.elementRef.nativeElement, 'keyUp');

而且你真的不需要另一个 fromEvent 因为你的 keyupStream 已经有来自 input

And you really don't need another fromEvent since your keyupStream will already have the value from the input

您的 enter 函数调用和您搜索的typeahead"函数调用必须包含在 observable 中才能取消它们.

Your enter function call and your search "typeahead" function calls has to be wrapped in an observable in order to cancel them.

鉴于他们是你可以做的事情

Given that they are you could do something like

const search$ = fromEvent(this.search.nativeElement, 'keyup').pipe(share());
const searchKeyEnter$ = search$.pipe(filter((e: KeyboardEvent) => e.keyCode === 13 || e.which === 13))
const searchText$ = search$.pipe(filter((e: KeyboardEvent) => e.keyCode !== 13 && e.which !== 13), debounceTime(500))

const mergeKeyDown = merge(searchText$.pipe(mapTo('search')), searchKeyEnter$.pipe(mapTo('enter')))
  .pipe(
  withLatestFrom(search$),
  filter(([origin, data]) => data.target.value.length > 2),
  distinctUntilChanged(),
  switchMap(([origin, data]) => {
    if (origin === 'search') {
      console.log('search started')
      return of('').pipe(delay(3000), tap(() => console.log('search call has finished')))
    } else {
      return of('').pipe(tap(() => console.log(' i got called from enter')));
    }
  })
  ).subscribe(() => { })

这里发生的事情是我们共享来自用户输入的事件

What's happening here is that we share the event from the user typing in the input

fromEvent(this.search.nativeElement, 'keyup').pipe(share());

以便我们分发它以创建和组合特定类型的新 observable

So that we distribute it to create and compose new observables of a specific type

仅使用回车键的示例:

search$.pipe(filter((e: KeyboardEvent) => e.keyCode === 13 || e.which === 13))

我们使用 mapTo这样我们就可以区分哪个事件被触发了.

We use mapTo so we can differentiate between which event was fired.

当这些事件中的任何一个被触发时,我们想再次重用刚刚使用 withLatestFrom.

When any of those events are fired we want to again reuse the value which was just updated from the input using withLatestFrom.

现在为了取消任何飞行中的异步任务,switchMap 运算符.

Now in order to cancel any inflight async task the switchMap operator can be used.

使用 Observable 时的一件大事是创建它们,以便您可以重用组合它们.

A big thing while working with Observables is to create them so that you can reuse and compose them.

我创建了一个 stackblitz,你可以 fork 并亲自尝试,注意控制台.

I created a stackblitz you can fork and try it out for yourself pay attention to the console.

https://stackblitz.com/edit/merging-events?file=src/app/app.component.ts

希望这会有所帮助!

这篇关于RXJS 处理两个不同的事件,其中第一次发生必须取消第二次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆