Check out a free preview of the full Asynchronous Programming in JavaScript (with Rx.js Observables) course

The "Close Button Observable" Lesson is part of the full, Asynchronous Programming in JavaScript (with Rx.js Observables) course featured in this preview video. Here's what you'd learn in this lesson:

After adding a close button to the search form, Jafar wants the click event from that button to dispose the current search stream and hide the form. To do this, he creates an observable from the click event and uses it in a takeUntil function.


Transcript from the "Close Button Observable" Lesson

>> [MUSIC]

>> Jafar Husain: So when searchButtonClicks happens, we know we want to cause a side effect. We want to show something on screen. But we also might want to use that as a stream of information to create our searchResultSets. Why would that be? Remember the goal here. It's when the search screen is open, we wanna begin listening to the clicks in the text, to the keys of the text box,,and start generating search results.

But as soon as somebody closes the search screen we don't wanna be subscribed up to that text box anymore. That gives us, again, the flexibility later on to remove the DOM elements and we won't have a memory leak. So let's go back to our searchResultSets and let's make it a little bit more complex.

Let's listen for the searchButtonClicks and only begin listening for key presses after a searchButtonClick. So see how I've used map to order things here. I'm saying, now, don't subscribe up to key presses all the time. Just subscribe up when somebody has clicked the search button.
>> Jafar Husain: So remember, map orders things.

It means that this function, this map function, is only gonna get executed when a click happens. It necessarily has to happen that way, right? This whole function can only get executed when we get a click event. Now I don't particularly care about the click event, but it will be passed in so that's why I just left it out.

In general, I recommend that. If you don't care about the input from a stream just leave out the identifier. Cuz it causes less calling it an overload rather than introducing identifiers, they're not used. Cuz some developer might look at that and assume that variable's used somewhere. For the same reason you wouldn't declare a variable and then never use it.

It's like an unused variable. So now we see that we're only going to begin listening to key presses when the search button is clicked. But how many dimensions is this collection now?
>> Jafar Husain: What data type is this? What's this whole sub expression?
>> Jafar Husain: What did I hear?
>> Off Camera 1: It's an observable.

>> Jafar Husain: Yeah, it's an observable So if we map over an observable, and for every entry inside we return another observable, how many dimensions are we?
>> Jafar Husain: Two, right? For the same reason that if I take an array,
>> Jafar Husain: Go back to the old example, and I do this.

>> Jafar Husain: I get
>> Jafar Husain: This. So whenever we map and then for every entry we return another observable, we end up with a two dimensional observable. And so that means we need to do what?
>> Off Camera 1: Concat.
>> Jafar Husain: Is it concat? So what are our options? Let's talk about the options and which one we should use.

>> Off Camera 2: It's always gonna be switchLatest. It's always switchLatest.
>> Jafar Husain: [LAUGH] Well, I don't know about always. I didn't say always but most of the time, certainly. Now, which one would we wanna use? Well, it might be switchLatest because obviously the next time somebody clicks a search button we don't want to listen to keep presses multiple times, right?

So somebody clicks a search button twice we don't to be listening to key presses twice. Which was effectively what happened if we didn't switch to the latest one, right? But now we have an interesting case here, we have an interesting case. Because switchLatest is great for those times.

You remember we talked about this yesterday. SwitchLatest is great for those times when you want to stop listening to an inner observable based on the same event that causes another observable to be created. So in this case, I don't actually just want to stop listening to the previous, to the text box, as soon as somebody clicks search again, I actually wanna stop earlier.

What's the event that happens that makes me wanna stop listening to the text box?
>> Off Camera 1: Close.
>> Jafar Husain: Close. Exactly. That's when I wanna stop listening to this particular observable.
>> Jafar Husain: Now that is not the same as a searchButtonClick. It's the close button inside of search when we display.

This is what I want to listen to. So how do I cause this searchButtonClicks, or technically the sub expression key presses? What I really want to do is I wanna complete that. I wanna complete this when the search button is clicked. So I only want to listen to the key presses until a search button is clicked.

>> Off Camera 1: You mean until the close button's clicked?
>> Jafar Husain: Excuse me, close button. Thank you. I only want to listen to key presses until the close button is clicked. So how am I gonna do it? What's the right operator to use here?
>> Off Camera 2: I mean it would be like our drag, right?

You start listening at the mouse down, you stop listening at the mouse up.
>> Jafar Husain: Right.
>> Off Camera 2: You start listening at the search button, you stop listening at the close button.
>> Jafar Husain: Yes. So what's the operator I use? What's the operator I'm using to drag it up?
>> Off Camera 1: Take until.

>> Jafar Husain: Take until. So what do I have to do? I have to takeUntil.
>> Jafar Husain: And I don't have an observable for the closes yet, so why don't we just create one of those?
>> Jafar Husain: Now I'm actually going to create it here. I'm not going to create it out here.

Why does it matter where I create it? So I could put closeClicks here. I could go observable from event
>> Jafar Husain: Is it closeButton?
>> Off Camera 3: That would affect what our function is.
>> Jafar Husain: I heard that would affect other functions
>> Off Camera 3: It's not the right scope.
>> Jafar Husain: It's not the right scope.

>> Off Camera 1: It only matters inside the form.
>> Jafar Husain: Yeah. Well, let's take the thought experiment that we only actually-. So instead of just showing this form, let's say, in this code when you click search we actually create the DOM elements, right? So, maybe at this level of scope, closeButton isn't even there, right?

So, but now here inside of here, is closeButton available? Yeah, because we know we've shown the search button, right? Now that's technically not entirely true because it relies on order. It relies on the order of this event handler just happening to execute before this function. And that's a dangerous place to be.

We don't want to be in that place. And so sometimes a pattern that people will use is they will have an ing and an ed, observable in their application. Search opening, search opened. So when you have a search opening observable, you can do some side effect and then cause a search opened observable to fire.

And then when you listen to the search opened observable, you can be sure that all the side effects that needed to occur in order to create those dom elements have successfully completed. Does that make sense? We'll come back to that. We're going to have to explain a new concept to explain how that works in just a moment.

So but for now, let's rely on the fact that just will happen to work this way. Cuz I believe it will based on the subscription order. So now that we've created closeClicks, we can do a takeUntil on closeClicks. And now, what's the right flattening strategy to use? We still haven't put in one.

We could use switch latest.
>> Jafar Husain: The truth is, all of them are- well, actually that's not true. [LAUGH] Notice, we still see the search button when we open the form, right? That's probably not the way it would work in a real application. You'd probably click search and then you'd move to this form, and you wouldn't see the search button anymore.

But who knows, maybe it's in the toolbar and somebody can click search again. So it's possible somebody could click search again before clicking close and then what would happen?
>> Jafar Husain: Probably not something good, right? We'd probably either create the DOM elements twice, which we wouldn't want, or we would avoid creating the DOM elements twice but subscribe up to the text box a second time.

So we wouldn't want that. So one thing we could do and one thing I would probably do in this instance, is inside of searchButtonClicks I would hide the search button.
>> Jafar Husain: That's one thing we could do, although let's not go that route. Another route we could go is we can just make sure to clean up the DOM element if it's there, or do nothing effectively.

If the search form is already visible that's probably a better way to go. Cuz you might have search in the toolbar, for example. So we can check to see if the display is already visible. I guess I can leave this logic out here, cuz we're not currently dynamically creating the DOM nodes.

But you'd imagine, if you were dynamically creating the DOM nodes, first you'd check to see if the DOM node existed, and then you wouldn't dynamically create it. Does that make sense? So now under that circumstance, the fact that somebody could click search again. What's the right flattening pattern to use?

>> Off Camera 3: Switch latest.
>> Jafar Husain: Switch latest, yeah. Because then they can click search as many times as they want, right? And then we were just gonna unhook from the event handler, and then hook it up again. So if I go,
>> Jafar Husain: Now here's a question, where does the switchLatest go?

>> Jafar Husain: Just want to make sure I'm all-. So to make sure we really understand what we're doing here, where do I put the switchLatest?
>> Jafar Husain: I put it here?
>> Jafar Husain: How many dimensions is this observable?
>> Off Camera 3: Three.
>> Jafar Husain: This sub expression right here is actually only one dimension because we've already switchLatest it.

We took every key press, we mapped it into an observable that represents a request. And because we we represented every item in the observable with another observable, we got a two dimensional observable. And then we had that concurrency probably wanted to solve and so we use which latest to flatten it out so we only got the latest result.

So this itself is a one dimensional observable. If I applied switchLatest to a one dimensional observable, what happens? What do we think? What happens if we concatAll, a flat array?
>> Off Camera 1: It errors
>> Jafar Husain: It errors. Yeah. ConcatAll, switchLatest, mergeAll they're all expected to work on two dimensional collections.

So I can't apply switchLatest here. Where would I apply it then?
>> Off Camera 1: The map?
>> Jafar Husain: Yeah, a map down here. Why? Because now since this is an observable, we know this is a nice flat observable. But we're creating it for every single searchButtonClick here, right? Because we're creating this observable inside the map function for every single time somebody clicks search.

So we create this observable. So we actually, at this level, we have a two dimensional observable. Because for every searchButtonClick we're creating the stream of search results from the server. And so we'll switchLatest. And that'll work regardless of whether the search button is visible or not.
>> Jafar Husain: Let's see if that worked.

>> Jafar Husain: Okay? Now notice, we still don't have our close button doing anything, right? That would be kind of ideal. We'd our close button doing something.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now