RxJS操作员技巧— startWith

rxjs 操作符

Photo by Pablo Heimplatz on Unsplash — Emitting a large number of events
Pablo HeimplatzUnsplash上的 照片 —发射了大量事件

RxJS操作员技巧— startWith (RxJS Operator Tips — startWith)

Creating awesome by combining startWith and EventEmitter

通过结合startWithEventEmitter创建超赞的EventEmitter

It’s quite easy to get lost in RxJS. There are plenty of operators to choose from and on my developer journey, I’ve discovered that it’s really easy to overlook an operator that might save you a headache or 2.

在RxJS中迷路很容易。 在我的开发者旅程中,有很多运营商可供选择,我发现忽略运营商确实很容易,这可能会让您头疼或烦恼2。

Following on from my previous article discussing flatMap vs. switchMap, in this article I’m going to cover startWith.

在上一篇讨论flatMap与switchMap的文章之后 ,本文将介绍startWith

The official definition from the RxJS documentation is:

RxJS文档的正式定义是:

Returns an Observable that emits the items you specify as arguments before it begins to emit items emitted by the source Observable.

返回一个Observable,它在开始发出源Observable发出的项目之前,发出您指定为参数的项目。

问题 (The Problem)

As usual, with programming, it’s too easy to get consumed by the “what” and not really the “why”, so we’ll start with a problem definition.

像往常一样,通过编程,很容易被“什么”而不是真正的“为什么”所消耗,因此我们将从问题定义开始。

Given I have a seed value that queries for a list of sub values, how can I subscribe to a Refresh Event that updates the list even when the seed doesn’t change?

给定我有一个查询子值列表的种子值,即使种子没有变化,我如何订阅刷新该列表的刷新事件?

That might be confusing, so, broken down into another way:

这可能会造成混淆,因此分解为另一种方式:

  • My support system shows a list of open tickets path/to/tickets

    我的支持系统显示了未售票path/to/tickets

  • An operator selects a ticket/path/to/tickets/:id

    操作员选择票证/path/to/tickets/:id

  • All of the comments inside that ticket are displayed

    显示该票证中的所有评论
  • When the Refresh Event is fired, how do I refresh the page?

    刷新事件触发后,如何刷新页面?

Yes, the user can refresh the page, but if we’re running in a PWA then it isn’t readily apparent how to do that as the browser chrome isn’t visible.

是的,用户可以刷新页面,但是如果我们在PWA中运行,那么由于浏览器镶边不可见,如何做到这一点并不容易。

The use of the Refresh Event is useful because it may not just be a button that refreshes the content. If you’ve wired your page to some kind of Web Socket system, you may need to refresh the list automatically on some external signal.

使用刷新事件很有用,因为它可能不仅仅是刷新内容的按钮。 如果将页面连接到某种Web Socket系统,则可能需要根据某些外部信号自动刷新列表。

I’m going to show you how startWith() can save the day here and create a really elegant solution.

我将向您展示startWith()如何在这里节省一天的时间并创建一个非常优雅的解决方案。

TLDR (TLDR)

Add startWith to your EventEmitter subscription pipelines and they’ll emit an initial value without having to wait for one!

startWith添加到您的EventEmitter订阅管道中,它们将发出初始值,而无需等待!

If you’d like a bit more detail, read on

如果您想了解更多细节,请继续阅读

设置 (The Setup)

Let’s start with a greenfield Angular project containing 2 routes, the Ticket List and the Ticket View.

让我们从一个绿地Angular项目开始,该项目包含2条路线,票证列表和票证视图。

门票清单 (Ticket List)

For this demo, the ticket list is very simple, it just contains a really simple array of ticket ids (1–9) and a series of <a> tags that navigate the user to /tickets/:id.

对于此演示,票证列表非常简单,它仅包含一个非常简单的票证ID数组(1–9)和一系列<a>标签,这些标签将用户导航到/tickets/:id

The boilerplate code
样板代码

The UI for this looks a little like:

UI看起来像这样:

票务视图 (Ticket View)

The Ticket View is where the “problem” is going to be solved. I will discuss 3 solutions for the Typescript code, however, the HTML will remain the same.

票务视图是解决“问题”的地方。 我将讨论TypeScript代码的3种解决方案,但是HTML保持不变。

The layout simply contains:

该布局仅包含:

  • Ticket Title Heading

    机票标题
  • Previous Ticket Button

    上一张票证按钮
  • Refresh Comments Button

    刷新评论按钮
  • Next Ticket Button

    下一票按钮
  • List of all comments

    所有评论清单

As the Ticket View is an Angular component, it will be possible to extract the ID using the ActivatedRoute class. An initial stubbed implementation of the Ticket View could look something like this:

由于票证视图是Angular组件,因此可以使用ActivatedRoute类提取ID。 票证视图的初始存根实现可能类似于以下内容:

The 2 UI Screens — Ignore the complete lack of design
2个UI屏幕-忽略完全缺乏设计的情况

示例—获取评论 (Sample — Fetching Comments)

In each of these samples, I will use the same method to fetch the actual ticket comments. Clearly this is just a sample as it doesn't actually do any fetching. However, by returning the result as an Observable<string[]> this will simulate an actual networking call as the Angular HttpClient will behave like an Observable.

在每个样本中,我将使用相同的方法来获取实际的票证注释。 显然,这只是一个示例,因为它实际上并未进行任何提取。 但是,通过将结果作为Observable<string[]>这将模拟实际的网络调用,因为Angular HttpClient行为类似于Observable。

丑陋的 (The Ugly)

Looking back at the problem statement “how do I refresh the list”, the easiest solution is to just refresh the page.

回顾问题陈述“如何刷新列表”,最简单的解决方案就是刷新页面。

优点 (Pros)

  • The list will refresh, job done

    列表将刷新,工作完成

缺点 (Cons)

  • The entire page will reload, including everything in it

    整个页面将重新加载,包括其中的所有内容
  • Additional server load

    额外的服务器负载
  • No ability for in-page progress bars or UX

    没有页面内进度条或UX功能

This means that all of the javascript, HTML and CSS needs to be recalculated for the entire website. And, if your users were in the middle of doing something else then their work could be lost. This is massive overkill for such a simple problem and defeats the whole purpose of a Single Page Application (SPA). This is the very definition of a sledgehammer to crack a nut.

这意味着需要为整个网站重新计算所有的javascript,HTML和CSS。 而且,如果您的用户正在做其他事情,那么他们的工作可能会丢失。 对于这样一个简单的问题,这太过分了,并且破坏了单页应用程序(SPA)的整个目的。 这就是敲击螺母的大锤的确切定义。

Additionally, by using the snapshot mechanism of activatedRoute we’re also unable to use the reusable components that Angular provides.

此外,通过使用activatedRoutesnapshot机制,我们也无法使用Angular提供的可重用组件。

坏人 (The Bad)

Next, we move to the slightly better, but not great implementation. In this example, we’ll use a BehaviorSubject to store the value of the ticket ID.

接下来,我们转到稍好一点但不是很好的实现。 在此示例中,我们将使用BehaviorSubject存储票证ID的值。

So, this is much better, but the “refreshing” logic hinges around resetting the value of the currentTicketId BehaviourSubject to the same value. This will make the pipeline think that the value has changed and will force the comments to load.

因此,这要好得多,但是“刷新”逻辑取决于将currentTicketId BehaviourSubject的值重置为相同的值。 这将使管道认为该值已更改,并将强制加载注释。

优点 (Pros)

  • The page doesn’t reload

    页面不会重新加载
  • Components are reused when navigating to different tickets

    导航到不同的票证时组件被重用

缺点 (Cons)

  • All elements using currentTicketId will recalculate on comment change

    所有使用currentTicketId元素都会在评论更改时重新计算

  • Need to filter out the initial null value on page load

    需要在页面加载时过滤掉初始null值
  • No way to tell the difference between a page load and a refresh event

    无法分辨页面加载和刷新事件之间的区别

Personally, I use BehaviourSubjects in lots of my projects. They’re a great way of converting a value to an observable and easily change them.

就个人而言,我在很多项目中都使用BehaviourSubjects。 它们是将值转换为可观察值并轻松更改它们的好方法。

But, I think we can do 1 better.

但是,我认为我们可以做的更好1。

善良(或可能是伟大的) (The Good (or, possibly the Great))

I’ve taken a long time to get to here, but we’re finally going to use startWith.

我花了很长时间到达这里,但我们最终将使用startWith

The Angular EventEmitter is a really simple observable. When the emit method is called (with an optional value), then all subscribers to that event can perform an action.

Angular EventEmitter是一个非常简单的观察对象。 当调用emit方法(具有可选值)时,该事件的所有订阅者都可以执行操作。

So, my ideal goal would be to somehow combine the incoming Ticket ID observable and the Refresh Requested event. The result of this would be if either the Ticket Id or the Refresh Event are triggered, the comments list is updated.

因此,我的理想目标是以某种方式结合可观察到的传入票证ID和“刷新请求”事件。 结果将是, 如果票证ID或刷新事件被触发,则评论列表将被更新。

I’m going to bring together 2 additional RxJS concepts to solve this problem the right way:

我将汇集2个其他RxJS概念以正确的方式解决此问题:

  1. Use the combineLatest operator to listen to both the ID and the Event

    使用combineLatest运算符来侦听ID和事件

  2. Add startWith to the event so it has an initial value

    startWith添加到事件中,使其具有初始值

优点 (Pros)

  • The page doesn’t reload

    页面不会重新加载
  • Components are reused when navigating to different tickets

    导航到不同的票证时组件被重用
  • Can separate out Refresh event from Ticket Id change

    可以将刷新事件与票证ID更改分开

缺点 (Cons)

  • ?

The benefit of using combineLatest is that the same pipeline can be used to retrieve the comments, no matter how this was triggered. This is a really useful function as it’ll take a number of Observable and return the output as a single array, but only when each observable has emitted something.

使用combineLatest的好处是, combineLatest ,都可以使用同一管道来检索注释。 这是一个非常有用的函数,因为它将接受多个Observable并将输出作为单个数组返回, 但是仅当每个observable发出了一些东西时才返回。

This is where startWith comes in. It ensures that the event always emits something so the comments will load in at least once.

这是startWith进入的地方。它确保事件始终发出某些内容,以便注释至少加载一次。

The Output — I’m not a UI Designer
输出—我不是UI设计器

好处 (Benefits)

I think the major benefit of this is that it makes using Events as part of pipelines really accessible.

我认为这样做的主要好处是可以真正使用事件作为管道的一部分。

There are many scenarios where you wish to load in data and have it refresh. Without this startWith functionality you’ll need to write quite a bit of extra/quirky workarounds which will just make your code ugly.

在许多情况下,您希望加载数据并刷新数据。 没有startWith功能,您将需要编写大量额外的/古怪的解决方法, startWith会使您的代码难看。

结论 (Conclusion)

The startWith operator can be useful in many scenarios. For any observable that you have which doesn’t have an initial value (which is nearly all observables outside of of() & from() & BehaviorSubject then having an initial value can make a lot of sense.

startWith运算符在许多情况下都非常有用。 对于任何没有初始值的可观察对象(几乎是of() & from() & BehaviorSubject之外的所有可观察对象,那么具有初始值可能很有意义。

Like most things, this is subjective. I’m a really great fan of “finding the right solution”, but, this doesn’t have to be your solution. Either way, the list of RxJS operators is large, feature-rich and extremely helpful with many scenarios.

像大多数事情一样,这是主观的。 我非常喜欢“找到正确的解决方案”,但是,这不一定是您的解决方案。 无论哪种方式,RxJS运算符的列表都很大,功能丰富,并且在许多情况下都非常有用。

What will you use startWith for?

您将使用startWith做什么?

翻译自: https://levelup.gitconnected.com/rxjs-operator-tips-startwith-d67109c8883e

rxjs 操作符

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值