Rx.js Fundamentals

Loading Data Over Time

Steve Kinney

Steve Kinney

Rx.js Fundamentals

Check out a free preview of the full Rx.js Fundamentals course

The "Loading Data Over Time" Lesson is part of the full, Rx.js Fundamentals course featured in this preview video. Here's what you'd learn in this lesson:

Steve live codes an improved search function that autocompletes, fetches, and displays the first value that matches the input search term. The enhanced function then makes a fetch request for the APIs expanded version of that matching value.


Transcript from the "Loading Data Over Time" Lesson

>> So, we implemented autocomplete, we could try a little bit of a different pattern. There's a pattern I talked about towards beginning, which is sometimes, you hit an API. With that API, you have to hit another API, right? You don't get all the data that you need. And sometimes, this is intentional, maybe it's a ListView, and you have some of the data and then you want to kind of decorate with more data.

Sometimes, it's just the API that you got, and it is totally on the backend team's backlog to give you a better API. Yeah, way, way down on the backlog. It's there though, and you got to do what you got to do. So these are both situations, both the positive, happy version of this and the sad version of it.

Regardless, how we might solve for it is a similar idea. So instead of searching on a keystroke, the functionality that we want now, so when I hit the Fetch button, and we're gonna get the Pokemon that matches. And if you saw before, we only got the name, the ID and the classification.

In this case, what we wanna do is we want to get that Pokemon, we'll show that data, and then we'll find out more. If I go to an individual one, if I say pika is 25, you can see I get a whole bunch of other information, most of which I don't care about, but some of it I do.

So we wanna change this a little bit, and we're gonna steal from a little bit of our previous code as well. But let's go ahead, and for now let's call this old search just so I don't have to deal with the comment in and out and moving it around.

So instead of the search input, that's in a form, and when either I hit Enter, or hit the button, it'll submit the form. That's gonna be when I go fetch one. So instead of an autocomplete, we'll maybe got a list of names. In this case, what we wanna do is, okay, we have found one that we wanna search for and return it for them, great.

So what we'll do is, well, first, we don't know the ID until the search results come back. We just want of the search results, the first one that matches. Of the first one that matches, we then wanna go get the subsequent data for that Pokemon, right? So we do have one more little helper function, this should just bring in right now called renderPokemon, that just shows a different, it's just a different DOM manipulation thing.

It just shows a div with a name and bigger letters and stuff like that. So, let's think this out. We have our search, And that is now gonna be from the form. And what do we care about with the form? What action?
>> [INAUDIBLE] Target.
>> Well, first of all, what event?

>> Input.
>> Input work for a Search field, but this is what we go submit, right? So we'll submit and then that will trigger our pipeline. And so there now, this is interesting because we care, we hit the form, we care about the input field, right? So we could say, To the search field's value.

So now this will be the contents of the search field just like we had on line 41 currently, great. And then, what we'll do is now we should add that value, cuz we don't care about the event target value, we care about what's in the search field. We'll go and we will get the Pokemon just like we did before, right?

So I think most of this will work, right? We wanna switch map, cuz this way they hit it multiple times, we'll get the most recent one. And you can argue maybe this should be an exhaust map, because go get what they got the next time regardless, they change it again.

Yeah, sure. Whichever one makes you happier. Again, sometimes this comes down to nuance on the behavior pattern. Knowing that both are an option, how they behave differently is the important part, which one you choose to use, again, depends on your situation. Okay, so what I will probably do at this point is I want to break out the act of getting a Pokemon that matches a search term from getting the additional data.

Cuz we know that search/Pika will get me everyone that matches P-I-K-A in the beginning, and then /25 will get me the data about that given one. So let's just separate those out so that our observable does not become insane. So we'll say const search Pokemon, With the search term, this will be very similar to what we have down there.

Right, and from there, we're gonna return this piece of it, right? It's basically this part of our functionality. I shall replace that wholesale right there. So we have the ability to trigger the search. We can take out the chaos in this situation. We can always add this chaos back in, don't worry.

And then for the additional data, or we can call it getPokemonData. That's gonna take either the whole Pokemon or the id, it's up to you. And then kind of return, From Fetch. And theoretically, I got two endpoints here, we could, let's actually shave that off real quick. And you probably stored these in variables, right, but this will work.

So the endpoint + search + searchTerm, and this one will be the endpoint, Plus the id that we're looking for. All right, and this part remains the same. So now we have basically factory functions that will produce observables scoped for the fetch requests that we want. You would do something very similar if you're using promises or what have you, just kind of go fetch that data, hard coding that and all over the place is a way to make yourself sad later.

So now, we know that when this happens, we have the search value. So what's the first thing that we wanna do? Again, we want to get the first one that matches the search term. We want to display that on the page, and then we wanna go and subsequently get all of the data about that Pokemon, right?

So what operator comes next in this search? Well, right now I'm just mapping the submission to the value and then moving on with my life.
>> SwitchMap?
>> SwitchMap, switchMap because I wanna go make a request, right? So take whatever happened, I've got now the value as what's coming in here, and we know that this takes a search term.

So we could actually theoretically, [SOUND] so, let's give ourselves some, prettier we'll handle this for us in a second, but let's give ourselves some room in the meantime. Make sure we have that comma at the end, we do. So we'll say, switchMap. What are we switch mapping?
>> SearchPokemon.

>> SearchPokemon, Right, and that should give us back exactly what we're looking for. Now, let's actually see, let's do this kinda piece by piece. We've got that in place, we're gonna turn subscribe just to a console log for now, so we can see everything in place. Suite, open up the console, bigger eyes at slightly, and so if I said, You can see we get back that initial data.

Awesome, right? We now have kinda what we need. Let's go ahead, and I'm thinking at this point, since we have that data, let's do some data transformation here. We'll say cool, now, I wanna pluck, Pokemon, so if I do that, we should just now get a raw array.

Now we only want the first one. Two ways I can handle this. One is I could use a map and just grab the first one out of the array. The other one is, we know that mergeMap will take anything observable like, and use from and put it into the stream, right?

And so, what happens if we use from and an array? From the very beginning, it's from and an array, what is-
>> You get a bunch of observables.
>> Yeah, I get a stream of all the values in there, right? So I could use something like, So I could use a map where I just grab the first thing on the right, that's totally I think I could do, or I could say, Well, figure out a way.

So we're just saying cool, map over this, return the thing, but then turn that array into a stream of values. It's effectively the same thing as doing, And then merging it together, right? We're saying take that array and stream them one by one instead of streaming me the array.

So we'll go ahead and say mergeMap, give me the series of them. So if I say, bu, now I get a stream of every Pokemon that starts with letters b and u. Now, in this case, I could just render those one by one, right? I'd no longer have an array that I have to iterate over in my DOM manipulation code, I could just one by one as they came through, but I only want the first one.

What can I do to get that?
>> Take(1).
>> Take(1). Take(1) will get me just the first one, bu. Fetch, I just get Bulbasaur, just not an array, not an array of one, just the raw object. Take(1) is common enough that there is a another operator does basically the same thing, does anyone wanna take a wild guess what it is?

>> First.
>> First, right, good. So first, yeah, the other part of the auto importing, are you auto importing from Lodash or RxJS? Bu, boom, all right, so now from the Search, I am switching to the most recent one, if I slammed on that button, we could put in all of that same logic that we had before.

But I'm getting the first one through, and now we could theoretically, Render it again, that's just DOM manipulation code because, trust me, watching me write raw DOM manipulation code by hand, it's no one's idea of a good time, so I did make sure it work before hand, right?

Right, you can see it loads to the page. But this is interesting, loading additional data. Now, that's hard coded, and cuz basically, unless we have the additional data on that object, it shows that, right? So now what we wanna do is, upon loading it, we want to then take that information and fetch additional data.

Cool, and so what would we do here? We have a few options, right? We kinda want to, this will render it and we'll get the Pokemon back where we can do more stuff, right? Cuz tap takes whatever you gave it and passes it along. Fair, well, what else do we wanna do?

>> We could switchMap on the getPokemon. We could switchMap on the getPokemon, right? Now, we could merge map, too, but you all have figured out the trick here already and why this becomes problematic. SwitchMap is probably our right choice, so I will not do the thing where I write the wrong code and trick you, cuz you've already guessed the right answer.

So we will switchMap to that Pokemon. And in there, what we can do is we can go ahead and we can get that one as well, right? So we've got the Pokemon in this case, and we could say, This code is gonna have some problems, too, right? So we could say switchMap and then we'll return, From that Pokemon.

And then theoretically, we could call render, what we need to do at this point is put it on to that Pokemon object that we had before. So, we've got that. So let's see what we have at this point. Console log at the end, right? So later I get that data.

And I could theoretically target that part of the DOM, but the other tricky part is like, I could do the first, I could render to the page, then I get this data. I could then also subsequently do that as well. And there's a few ways that I could do this.

I could target that and that would probably be enough to get me the data. The other thing I could do is, I could kinda try to do them little bit both at the same time, right? So what if we tried this? We'll take our switchMap where we have the Pokemon, and we're just gonna store that in a variable for a second.

Now, we know that if we wanna do anything in RxJS, we have to turn even our raw values back into observables. It's kind of like asynchronous code, once you start playing with async, everything has to be async, right? The second you have one synchronous function in there, you don't have async code anymore, or you just have a headache or bugs.

So we'll say, Turn that into an observable. And now we've also got, We could go ahead and we could say, Our additional data. We just call data if you wanted to. Our additional data is get that Pokemon data with that pokemon, And, Put it all together. So here we can just map it.

This is kinda what we did with scan earlier, right? We can say our data. So we've got the Pokemon in scope, we've got the data in scope. Everything about the pokemon plus the data, nope. Right, and then out of here we need to return both of those at the same time.

So, it'll pass through that initial one, and so here we could actually move this down, so we'll start fetching our data. Let's try this out. You can see that it loads, right, in two passes. Now, there is an issue here where if we slowed this down, we'd be getting all of this, we'd be doing all this, and then they could theoretically.

So we can use a switch map or an exhaust map depending on what we wanna do. If you're like, finish what they did, for some reason, an exhaust map will pull this all together. But if we look at this, let's review. We go get the first one, and then we're gonna kinda fire.

We can take showing the initial one, if it comes back super fast, cool. Do both but then kind of give me both pieces of data, because when I did them in a stream, that problem with the pipe, cool. You had the Pokemon and that data, and then you do the second one, then you only have the second piece.

Here, we're just gonna kind of like, I have my Pokemon. I'm gonna go get more stuff. And then, I'm going to merge them both here, and then keep emitting data, so I can kind of update my data store. You could probably do this with a scan as well, but they're just kinda different ways to kinda manipulate the streams and pull them together.

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