Collect, Combine and Cache RxJS Streams for User-Friendly Results by Deborah Kurata

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] thank you so are my can you hear me okay yeah great okay so we're gonna talk about collecting combining and caching rxjs streams and I have a link to the slides and the code at the end so you don't have to be trying to write down what you're seeing my name is tarik Khurana case you didn't get that from the beginning I'm a software developer I'm a consultant I'm also a Pluralsight author a Microsoft MVP and a Google gde so that's pretty exciting so we're going to merge our streams here this more this morning this afternoon this evening whatever time of the day it is I don't know it's like 9 o'clock in the morning so what we're gonna do is we're going to look at a declarative approach for collecting combining and cashing our streams with rxjs and the important part is that we're going to do it with no subscriptions and the best part about no subscribe is no unsubscribe that's exactly right so you don't have to have some kind of complicated code to make sure your unsubscribing from everything so we're gonna take a look at how we can go ahead and do that all right so our sample application is pretty simple because we really want to focus on the code so on the left hand side we've got a way to display a list of things the user can pick something from the left-hand side and it displays the detail on the right relatively straightforward so that's kind of the app we're going to be looking at for this talk so we talked about the fact that we're going to look at collecting combining and caching so this is our collecting now we've probably all learned our current pattern for retrieving data right we have some kind of get method and a service here's my product service with the get method it is simply returning the result of an HTTP GET request and in our component we have implementing and aaniin it and then writing our ng on init that calls this we dump it into an array of some kind and bind to that array okay so we're all familiar with this pattern yeah I know that's what I teach in my intro courses and it's probably what you learned that's how a show you in the documentation and everything that's how you retrieve data I'm going to propose a different approach and that is a more declarative pattern for retrieving data so instead of defining a method for retrieving our data we're going to simply define a property so we have products dollar and the convention is to define an observable with a dollar at the end so that you know you're working with observables and instead of then having a method we're going to simply assign the result of our HTTP GET directly to this property okay that make sense so far and we've got a little bit of error handling there all right so what does that mean for our component well in our component then we don't need to implement any net we don't need ng on in it we don't need procedural code in here we just define another local property in our component and assign it to the property from our service easy enough again so far right even at 6:10 in the evening or no.6 let's see 6 and 12 18 18 10 okay so now that we have a product dollar here in our component we're gonna have to bind to that instead of our products array so we do that using the async pipe so the async pipe does several things for us first of all it automatically subscribes when the template is loaded and it automatically unsubscribes when we leave the component when the component is destroyed in between times once it is subscribed what it does is that loads the result every time the observable emits something it loads it into the variable so that's what that as syntax is doing by saying as products every time that stream emits something it's a sign to that variable then we can use that variable anywhere else in our template so it makes the rest of our bindings sort of look like we just said bound to our regular array so so far we're not talking about anything you know really complicated right why why would we want to do this well one reason is we can then use on push change detection so if you want to improve the performance of your application by using on push you can do that by simply moving to this technique why does that work well the on push change detection minimizes that change detection cycles with on push it will act it will check for changes anytime one of three things occurs one of your nested components has an input property that changes whether it when it has an output property that's the emitting an event and the third one which is the one important for our case when an observable when it's bound to an observable and now we're binding to our observable stream so we can be using our on push change detection so we get better performance so using more declarative more reactive it gives us improved change detection maybe that's not enough maybe that the performance isn't that big of a deal why else would we want to use this technique for managing our streams another reason is we can leverage more thoroughly the rxjs operators at last count there were a hundred and four I don't know if anyone's counted them recently to know if there are more than that but there are a hundred and four different operators you can do everything from accumulating information to filtering to all sorts of different things with these operators you can also compose your stream so we'll see how to do that here in a moment basically what I mean by that is sometimes our user interface needs data from multiple sources and we can combine all of those together and lastly we can better react to user actions so as the user is doing something clicking on things whatever we can more easily react and we're gonna see how to do that as well okay so that was collecting so we've got our collecting of our data we now have streams all the way from our service through our component to our template how do we then deal with combining our stream with other streams and how do we work with that all right so come to find out when we get our product data we're getting the category as an ID number so our API endpoint that we're calling to get our product data is giving us a category ID 3 or 17 or 45 or whatever and we could just display that to the user and the user can go I don't know what a category 3 is right what we really want to do is give them the category the name so how do we go about doing that so we want it to be something like toolbox or garden or games or you know whatever our correct category is to be more user friendly well we happen to know we have another endpoint that gives us a lookup table and we can find that category lookup table that has our IDs with our names so we can declaratively define the category stream so again this is code just like our products dollar code only now we're getting products categories we're getting calling a different end point but again it's a declaratively defined stream ok very good yeah all righty so then what do we do then we need to merge our streams and there are several different operators that we can use with our XJS in order to merge our streams together the one that I like for this kind of stuff is combined latest combined latest is actually a creation function so you don't to pipe it into something have something and pipe through something you can use it as a static function and just call it and pass in the streams you want to combine now in this particular example I'm only combining two but you can combine any number of streams we'll see another example in a few minutes that I'm combining three but again it can be any number so what does combine latest do well it combines multiple streams into one stream that sounds pretty obvious but it has a couple of specific things that it does first of all it uses the last emitted value from each stream and it also waits for each stream to at least admit once before it emits and as we all know an HTTP GET is a one-and-done right it returns your data and completes so this combined latest will actually wait for both sets of data to be returned before it moves on so that's a good thing right because we don't want to try to match up products and categories when we don't have the products and the categories yet so this will wait for both of them to complete now when products dollar when that stream emits it's getting an array of products when product categories emits and gets the data back from the HTTP call it's going to give us a array of categories product categories and what combined latest emits is an array with the results of each of the streams so the first element in the array is the result of the first item the first stream that we passed in the second element in the array is the result of the second stream we passed in are we good with that yeah okay so you can kind of think of this as almost like a method right we need both bits of data passed into our method in order to be able to manipulate them and do the lookup and have to work with them so you can kind of think of combine latest as a way to pass and a bunch of stuff that you can work with together is that making sense okay so our combined latest is going to emit then this array so what we can do then is map that data so that we can do this lookup so the first thing I'm doing here is using array destructuring to assign a variable name to each element of the array because it's easier than using the array syntax what is array zero versus array one what does that mean here I can actually give them variable names then I am using the products array that this is giving me back and I'm using the array map function to map each product in that array what am i doing with each product in the array I'm copying it using my lets the array map method I'm copying it using the spread operator so I start with a product I create a new product that's a copy of the old one and then add another property to it so I'm defining this new category name property I just called a category so it fit on the slide better but that's our category name and I'm using the second array that was passed in that categories array to find the right name based on the ID that I have seems pretty straightforward now the last important thing is to define the resulting type if you don't add the as product if you hover over the result of products with category it will tell you it is of type curly brace curly brace property property property property property and we don't want that right we want coming out of this thing a product array so by saying as product as it's mapping each of these products in the product array you will end up emitting from this whole thing and you'll have an observable that emits a product array which is what your component is expecting to get but now it just has an additional property is that making sense okay so our ponent remember when we looked at our component before we were selling products dollar equal to this dot product service dot products dollar now we're going to instead set it equal to this dot product service dot products with category dollar so now it just has our same product data but now has this additional category property on it since we still have products dollar there on the left our binding all still works all we have to do in our UI is add or change where it was displaying category ID to display the category name instead so what we end up with is a much more user-friendly display and we don't have a lot of procedural logic going on this is all just done with our streams okay what if you need to combine nested observables so the first question here is what do we mean by nested observables in this case we have suppliers that were showing in addition to our products so with our product data is coming some supplier IDs so apply our ID seven and fifteen or whatever but we don't want to show the user hey supplier fifteen and twenty five or whatever we want to show the actual list of suppliers now the same thing would be true with customers and invoices orders and order line items any kind of related data like that so how do we deal with that well nested subscriptions are kind of the anti-pattern which you're not supposed to do so this would be cool code that we might have done earlier where I am getting the product I am mapping it to pull the supplier IDs out and then for each supplier ID I'm doing another HTTP GET to get that supplier and then I've got multiple nested subscribes in here you see I've got the SUBSCRIBE and another subscribe so that's an anti-pattern it's not something that's recommended because of the complex nature of having them inside each other plus you lose your stream going to your UI at this point so what do you do instead well you can use the higher-order mapping operators and I'm going to be looking at three of them the first one is concat map and I've got a little thank you here on the bottom so I just kind of simplified the example to demonstrate it here when you think of concat map think of waiting soken cap map weights what does that mean well I'm doing of 15 and 42 so this is I'm just kind of simplifying the example saying I have supplier ID 15 supplier ID 42 I can use concat map to do the getting and what will happen is when 15 is emitted it will go get the data for supplier 15 when 42 is emitted it's going to just sit there and wait once 15 is retrieved then it will go get the data for 42 so it's going to do them sequentially and then you will always be sure that your data is emitted sequentially and up so I just saw a typo on my slide that should be an S over there next to my 42 anyway okay so that's a concomitant between that and merge map is that merge map runs in parallel so if I do an of 15 and 40 to 15 we'll start as soon as 42 is emitted that retrieve will start and it will get them immediately as soon as I can it may give them to you then in order or it may be out of sequence depending upon whether one call was potentially faster than the other so you can't be guaranteed a sequence here now of those two for our scenario which one makes sense I want to get all the suppliers for a product do I want merge map or do I want concat map merge map because there's no reason I worry about sequence I don't care about the order so I don't need concoct map concat map is best for things when you do want to ensure they're done in sequence like say I was doing deletes instead of retreat or updates I might want to ensure that they're done in order so that my updates don't go out of order and potentially mess up something or my delete merge map is best to do when you don't care about the order I might in my code have my own pipe to sort it you know by alphabetically or whatever so I don't care what order that these suppliers are coming back in the last one is switch map and switch map always switches to the most recent one so again I have of 15 and 42 when it when 15 is emitted it goes to get 15 and before it's gotten it 42 comes along and says okay never mind on the 15 I'm gonna go get the 42 instead and again this one would not work for our scenario this is better for things like type-ahead like the users typing and every time they type something else you want to reget and basically forget about what they what had already been started it's also useful when you're handling the click on the left hand list you know I clicked on the first one no no I'm at the third one so that it just processes the last one that they've picked okay so those are the three to help you deal with kind of nested information so what we end up with then is our suppliers here on the bottom okay so one of the other things we haven't talked about too much yet is actually reacting to user actions so here when they use their clicks on the Left on the hammer we display the details on the right hand side so how do we make that work we want to be able to have our streams react to user actions without having to write too many procedures to make all of that work so how do we do that well I've basically defined three steps in order to have our streams react to actions so the first step is to create a stream that I've been calling an action stream I got help on Twitter I don't know how many if you saw that tweet stream on there I was coming up with a bunch of different names and we kind of settled on an action stream then combining our action stream with our data stream and then lastly emitting a value to the action stream every time an action occurs that was kind of a logical name I'm glad someone suggested it okay so those are our three steps so let's try them out we're going to start by creating an action stream how how do we create an action stream well there are numerous ways to create a stream but what you'll frequently see for action streams is I'm using subject or behavior subject subject is a special kind of an observable a behavior subject is a special kind of subject the key difference between a subject and a behavior subject is the behavior subject has a default value so if I was creating a new behavior subject I would have to be passing something into the constructor so if your action stream requires that there be an initial action before any other real actions occur some kind of initial value then you want to use behavior subject instead okay so you have the ability to create the new subject or the new behavior subject and you can use the generic syntax to specify the type that you want now in our case the user is picking a product on the left and what we want is we want that products ID so we're going to define our stream as a number so every time the user picks one we want the number the ID of that particular product that they've picked so that we can go display that on the right-hand side now how is the subject and behavior subject different from an observable well first of all they're multicast so they can cast to multiple subscribers normally your observers are all unicast so if you subscribe then you get the results and that's it so that would be instead of me talking to each of you I'm going to call each of you individually and tell you of my talk which is not very efficient we instead you know want to broadcast it out to everyone all at the same time the other thing that's different about a subject behavior subject than unobservable is the fact that a subject and behavior subject behave both like and observable you can actually get at it's observable with as observable but also as an observer and an observer is basically a object with next air and complete methods in them normally when we subscribe we're passing in the observer with the next air air incomplete methods but our subject and behavior subjects also implement the eye observer pattern and have the next error incomplete methods that we can call on them okay so that gives us control over being able to emit items into our stream being able to specify an error that we want passed along or to complete our stream all right so that's an action stream so what we end up with here is product selected action is our subject or behavior subject if we want an initial value and product selected action dollar is the as observable that is associated with that subject or behavior subject okay we're good you guys are really awake for this hour of the day it's amazing all right so all right so our three steps our first step was to create a action stream so that's what we just did our second step was to merge it with our data stream and guess how we're gonna do that with combine latest again so now we have our two streams here what we want to show on our left our right let's see that's the right the right hand side of the page is we want to show the detail so and we want that detail to include the category the actual category String not the category number so we're going to use our products with category stream the one that we've already gotten that category mapped correctly for and we're going to use our action stream the product selected action we're going to pipe that through and again we are using array D structuring so that first stream that I'm getting is going to emit an array of products so I'm calling that products the second stream that I have there is emitting the product ID of the product that the user clicked on so I'm just calling that one selected product ID okay all right so the last part then I'm doing a find on that products a way to find a particular product now if I was going to be doing an edit or something I might actually want to call an endpoint and get the most recent one but since I'm just displaying the data instead of hitting the server again I'm simply going to find the product that they picked in the list I've already retrieved from the server okay so that was our second step so we collected we created the action stream we combined it with our data stream our last step then is we want to emit a value on an action so in our template when the user clicks on one of the products on the Left we're going to bind our click to unselected so in our component we're going to have a non selected method our templates going to pass in the ID of the one that they clicked on and it's going to call a method in our product service our product service code then is going to call our subjects next method you notice that one doesn't have the dollar sign on it so that's our subject version and we're going to call next which basically takes that selected product ID and emits it into the string so here is the picture in kind of a marble diagram ish kind of way so our first stream there is our one in done data stream that's our list of products that we're getting the second stream represents our action streams so the user clicked on the one we don't get anything emitted from our combined latest until both of them have emitted something which is why you might want to use a behavior subject if you want it to be able to start with a consisting value then it emits the combination of those two things when the user picks something else it reads and what happens when it re-emits is it reacts Acutes your entire pipeline so next time that selected product selected action emits something else it reruns your pipeline and refined the new product and since it's all bound to the cup it's set in the prod in the component bound in the template it's going to have change detection automatically have it display the new product okay so all we are doing in our code is when the user clicks on something we're throwing something into the stream and everything else handles itself okay we don't have to tell oh I need to tell this that it changed or that that it changed or whatever okay caching now if we just have our our system as we've defined it so far when the user clicks on product list it's going to do our are binding to our observable which is going to kick it off and it's going to go get our data if the user then goes to the homepage it's going to destroy the page which loses all of our data if they click on product list again it's going to go back to the server and get our data again now if you have a highly transactional system that might be really desirable but maybe in our company our product line only once a month once a quarter we don't need to be getting the data every couple of minutes as the user moves around so how do we deal with that well we in the old days like yesterday we we might have cashed that in our service because of its data cached in the service we can easily share it but now that we have everything in these streams we can use the share replay operator so the share replay operator takes our data that we've retrieved so here it will be our product data that we've retrieved and it puts it into a replay buffer so even if the user leaves the page when it comes back it resub scribes and it replays and I have a replay of one so it will replay that last omission which is the data that we had retrieved so it remains active even though there are no current subscribers which will be the case if you go to the home page so a really handy little operator okay so the last thing that I have is two observable all the things okay what do I mean by that all right so our right-hand page has not only our product data and our category it also has our supplier data and it has that title on the top notice that title also has the product information as to which product is there so that means we have to have some kind of code to update that every time that the product changes how do we do that well in our component we can simply use product dollar because now we're on the right-hand side that's the detail it's the one product we can assign that to the selected product that we've already figured out from our action stream combined with our category stream and so we've got our product dollar all set all ready to go to bind to to show our product data with its category we can also take our page title and we can make that an observable we can take our product stream and use the map to transform a product into a simple string okay so that's going to be our page title and every time this dot product is modified which will occur every time selected product changes which occurs every time something omits into that action stream it will automatically change our page title and product dollar our selected product that we've displayed and then of course we've got our product suppliers coming from our product service where we used our merged map to get the data I have the full code for this in my github and I'll have the link at the end ok so now you have all of this so we're going to merge our streams all of the streams that we need for our user interface because we don't want to have sixteen aceing pipes in our template we want one so we're gonna merge all of our streams together and again a thanks to Zander for showing me this technique that I then stolen and presenting to you all so here is the combined latest where I'm showing combining three things we've got our product stream we've got our product supplier stream we've got our page title stream depending upon how much else we're showing in this UI we can have more streams here then I'm piping that through the first thing I'm doing is filtering for a null product that way it doesn't try to display anything if nothing's been picked yet because in our application when the app first comes up nothing's picked we don't want it to try to display anything if there is a product what I'm doing is again using a ray destructuring assigning a variable name to the result of each of the streams and then I am basically transforming that array with named elements into an object now why am i doing that because it's a whole lot easier to work with objects in our template than array elements again with bracket 0 bracket 1 whether those mean so here I'm creating an object from my array then in my template I have one pacing pipe piping to this VM async as VM and then I'm just accessing these properties VM got page title VM dot product VM dot product suppliers that VM is kind of a kick back to the old angular one right remember that yeah so it's it's sort of lovingly remembering so what do we think is this something worth trying yeah what do we think like that okay so there are some cons of course to this approach picking the right operator is sometimes not easy difficult to debug but there's a lot of benefits as well all right so with that here's the link to my slides my code and feel free to follow me on Twitter and thank you all very much for coming I appreciate you staying this late with me thank you [Applause]
Info
Channel: ngVikings
Views: 3,184
Rating: undefined out of 5
Keywords:
Id: HE-xh_RBIno
Channel Id: undefined
Length: 34min 16sec (2056 seconds)
Published: Wed Jun 05 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.