Advanced Angular: Performance & Enterprise State

Reading State with rxMethod

Alex Okrushko
NgRx, Angular GDE
Advanced Angular: Performance & Enterprise State

Lesson Description

The "Reading State with rxMethod" Lesson is part of the full, Advanced Angular: Performance & Enterprise State course featured in this preview video. Here's what you'd learn in this lesson:

Alex demonstrates creating a card store alongside the existing card service, setting up initial state and root-level providers, and adding a request status feature to manage API calls using rxMethod.

Preview

Transcript from the "Reading State with rxMethod" Lesson

[00:00:00]
>> Alex Okrushko: So now our request status feature, custom feature is ready. Let's create the cart store instead of the cart service, so we have the cart service here. I'm going to create the cart store. I created side by side here, a new file, cart store.ts. So let's see what we're working with, so we can do interface just to be consistent, CartState.

[00:00:43]
Is what do we handle here? Interface, the same thing what we're handling here, right? We are responsible for ticket IDs. So there's no difference. We're going to be responsible for ticket IDs, ticket IDs, and they are also a type of string, right? So the same way we have the signal for. Let's now export our constant cart service, and again, see, by the way, one more thing to note.

[00:01:22]
Constant, I'm creating this variable with the capital letter CartStore, capital C, right? Even though it's a result of this signalStore call, right? This constant providing with the C capital, why do I do that? Because at the end of the day, signalStore will return me at this class, right? They'll do a lot of things, right?

[00:01:50]
We saw the sources and they'll return me the class itself. So just to be consistent with the rest of the injectables we do, we can just keep it with the C, and because it's really under the hood returns me a class, it just dynamically constructs that class through the functional approach. So this will be provided in root.

[00:02:21]
Provided and right. And we'll do some with state, that's the state that we are responsible for, which is our cart state. And our initial ticket IDs is empty, all right. So now we have the state. We had a computer here, remember that was our account. So withComputed. Let's get our tickets extracted. TicketIDs extracted count, basically our tickets.length.

[00:03:25]
So we have our computed. Now, we'll add a custom feature that we just had created for us, which is like request status. So now become part of the store as well, so with requestStatus. And I see here I used it before computer, it doesn't matter where you use it, you can use it here, or you can use it here.

[00:03:50]
WithComputed, it is not using anything for request status, so it doesn't matter which order they'll add things together. But what does matter is that we'll use the request status before we use it with methods. And this is what I'm going to introduce, something really special. So in our signalStore, we'll just add withMethods.

[00:04:25]
And with our withMethods, we want, let's get our store here. In our methods, we want to add when you want to do the API calls to load and to add to cart, which is basically, post this event there. The way we're going to do this is a few ways. First of all, we're going to inject whatever we need to inject in this function.

[00:05:08]
So I'm going to do const httpClient, I'm going to inject this, HttpClient. And then, or we can even do things even more interesting way, but let me just show you this way first. So, and then we can also need the ticketsURL, which can we inject here. There you go. So now we'll need to return our methods that we're going to be adding.

[00:05:53]
The first method that we're going to do is a load method. And here, I'm going to do a little cool trick, I'm not going to just return the callback, like function, I'm going to return something's called rxMethod. And rxMethod turns this load right into the function, into the method that can take a value every time this method is called.

[00:06:24]
And what it does internally, it creates the almost observable pipeline and pushes values into that pipeline. So let me show you in the results here, so first of all, we'll tell load doesn't really pass anything, so we can say this is a void, right, so it doesn't provide anything. RxMethod, it should be imported.

[00:06:55]
Where is the rxMethod should be directly from our signals? Let me see. So let's start with method. Or maybe it's our import. From Jerk signals RXJS interop. Yes, here it is, rxMethod, it's from the RXJS interop, and again, this is super powerful, every time we're going to be calling like this load, for example.

[00:07:30]
Every time we're going to be loading, or if we have passing some value here, for example, say we load like specific ID1, specific ID2, specific ID3, for example, and we can say that, hey, it's actually taking the string, right, for this. Every time we'll be invoking it, it will push this value into the pipe, into our observable pipe.

[00:08:08]
So let me show you a sample. So right now we'll work with void because load doesn't really care about the values. And here within the load. We're going to create the pipe. So what the pipe will do, pipe is RXJS operators, so we need to provide one of the RXJS operators at the top. Pipe allows us to group operators.

[00:08:35]
So the first thing we're going to do is, this is the tap operator as well, again, this is a side effect operator, just calls something and doesn't do anything else, it's like a side effect. So in this side effect, what we're going to do is we're going to patch our state with store with setPending. So remember, patchState takes the store, and we need to change something in this.

[00:09:16]
SetPending is one of those pure functions, so what it's going to do is it's going to set the request status to pending. So this is our custom version of basically resource knowing, being aware of the status of request, right? So before we do anything, let's just say, hey, this is pending. And this pattern that I'm showing you is almost so standard, yes, so standard.

[00:09:50]
So then, what do we do with load? When we are asked to load the tickets, what do we do? Well, we can choose, this is the place for us to choose one of those operators. SwitchMap, exhaustMap, mergeMap, and concatMap. This is the place that allows us to explicitly control how we are invoking APIs. This is the superpower of rxMethod.

[00:10:18]
That's pretty much the reason we would want to use it anyway. So in this case, I'll say, hey, whenever there's a new load request, cancel the previous request and start the new load request, I want always the freshest data. Now, in your case, you might want to say, hey, if you're already loading the data, and there's another request comes to load another data, don't load it, I'm already requesting it.

[00:10:48]
So it's very much up to how your application logic, how you want to cancel it, but this allows you to not just be at the mercy of the user and the other controls, but that does the mergeMap by default. But you are in control of the destiny of your application and how your race conditions are handled. So in this case, just for this example, I'll use mergeMap.

[00:11:15]
Reality, in most cases, if I'm loading the data, I'll probably use exhaustMap, right? Because I'm like, hey, I'm already loading the data, you want more than I'm probably getting you the freshest one, unless I have a good reason to always refetch the new one, right? So in this case, I'll just use the switchMap, not because I want you to use it by default, but because I just want to show you because the other one will be exhaustMap, so we'll choose to cancel any new load request if we're already loading something.

[00:12:03]
So the switchMap will be here. And in this case I'm going to use my the http.get, this is HTTP, it's not, it's not this is HTTP, it's just HTTP because it's a property here. And I'll show you cool other cool things where I can put this, typically try to put it up in the method or there's other ways. So we'll do the get for this ticket entry.

[00:12:42]
This is the same thing that we have in the service, right, that's, we're getting the data, the solo tickets, that's this, right? So we're going to do the get the entry. GetTicketEntry. Is it, is it, let me see if it's, no, it's not, interface is not exported, so let's export this interface. So now we can use it in other places, GetTicketEntry.

[00:13:18]
And this is the array, right? Now, within then switchMap will have this inner pipe, get ticket, we need to have this ticket, of course, ticketURL. That's what we call, like this returns us an observable back. And it returns an observable of all of those ticket entries. Before we flatten the result, as we just learned in the module 3.

[00:13:48]
What do we want to do? We want to catch, we can actually handle both success and the error right here, so we're going to do the pipe, that's our inner pipe. And then, you would typically have again, tap that will patch the state and you have to catch error and all these things, that's the successful and the error handling.

[00:14:37]
It is so standard way to deal with this result that on the indirect side, there's an operator that just can help you with this, and it's called tapResponse. So let's use the tapResponse operator from Jax operators. The tapResponse will allow you, tapResponse will allow you to handle the next in the error.

[00:15:12]
So on the next case, what do we do on the next case, which means we're getting our tickets back. I'm going to patch my state with those tickets. So I'll go to patchState of my store, right, we need to have to update these tickets, yeah. So the way I'll do this, again, this is the, what I'm patching it with, is ticketIDs, and here it comes as a ticket entry, I need to do exact same thing as I was doing in the service, right, which is basically mapping the data and then mapping the data here, right?

[00:16:02]
So just a ticket ID. So this is going to be exactly the same thing, so I'll have my tickets. And I just map, because it's a more complex object, I only care about the event IDs here, right? So this basically becomes an array of strings. So this is our next. And then we'll have our error case, and notice CI didn't have the error case, I was complaining, because in the tapResponse, we really want you to handle the error case as well, we know how bad that is, if it's not handled, we need to handle the error case.

[00:16:48]
So in this case, I'll have my error, unknown. And in the error case, I want to patch my state, oh, by the way, here one thing I patched with tickets. But I also had my state and pending, right? So I want to make sure that we kind of close that as well, so I'll do the setFulfilled in this case, so I can provide multiple updaters here in the patchState.

[00:17:16]
I could have two patchStates, but I can just provide multiple updaters independently, and this will update the ticket IDs, and this will update the request status, so it's fulfilled, and then in the error case here, I'm going to say, setError, which is one more last function that the helper function we had there, I'll just say error.message, right, so let's just say error error had the message, for example.

[00:18:04]
Message string. For example, so patchState, error message string. And you mean type you a message, it does still have 3 years here you go. So that's our load again, just let's review what it does. Every time the load is invoked, we're going to get here into this pipe, and every new one will say, hey, it sets the pending anyway, it was pending, it will reset the pending, doesn't matter.

[00:18:32]
Now, we have a request, the first one will start the request. And until this request is busy, if there's another one coming in, it will stop this request and start another one. Once things are coming out of this HTTP requests, either success or the error, our tapResponse will handle the next, which is success, and error if it's an error.

[00:19:01]
We also have another one that's called finalize here, so this is if you want to handle something in both success and error cases, yeah, but in this case, we're setting fulfilled for success and we're setting error for the error case. This is such a standard way to deal in the rxMethod, the only choice for you is to really control how you want to handle these race conditions.

Learn Straight from the Experts Who Shape the Modern Web

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