Lesson Description
The "Create the Cart Service" Lesson is part of the full, Intermediate Angular: Signals & Dependency Injection course featured in this preview video. Here's what you'd learn in this lesson:
Alex creates a stateful cart service for ticket management that includes optimistic UI updates. This gives a better feeling of perceived performance since changes are reflected in the UI immediately.
Transcript from the "Create the Cart Service" Lesson
[00:00:00]
>> Alex Okrushko: Let's import it where we want. So we provided here already, we've done the sub config. So let's create the cart service. Let's create the cart service, we'll create it in the core as well. Cart service.ts. Cart service.ts, that's right. Let's create the cart service. We'll be working with the ticket entries, so we'll just create this interface as well, that's what we're going to be saving.
[00:00:39]
The ticket will have basically two IDs, one, its own unique ID by when the ticket is imported, inputted into the database, and event ID that they'll be referencing as well. So we'll just create the ticket entry. This will allow us to save tickets to the database as well, as we buy them. All right, so the ID would be, this is the ticket ID and then the event ID here as well, string event ID.
[00:01:17]
Now we'll create the export class, we'll create a class of the cart service. This class will be injectable and this class will be also provided in root, so it's a singleton class. But this class will be a little bit different than our event service, because this class would be stateful, it will keep some state. But before we jump ahead of ourselves, let's make sure, what do we do here?
[00:01:57]
So we want to be able to click on the buy ticket and we do not want the API to go all the way back and forth before we see the ticket number increases. We want to see it immediately, right? And this is something that's called optimistic UI update. Very powerful thing in the software engineering and the front-end engineering, this makes our app feel almost instant, right?
[00:02:31]
So what we're going to do is we're going to increase the ticket that you already have and then send the request to the backend to add that ticket. If addition of that ticket was successful, then we're like, well, we're done because we already added that ticket to the UI. If it's not, then we need to kind of revert it back. So it's really powerful technique, it kind of needs to have a few conditions to be successful.
[00:03:06]
First, usually there should not be a lot of checks at the backend for this API to succeed, right? I mean, tickets kind of like is available, there's probably a lot more checks, but in general the optimistic UIs are really good for like, you know, liking things, right? And things like that. There's not a lot of checks, that's first of all. Secondly, the chance of success of this event should be pretty high.
[00:03:35]
Another one is the response from the backend should be within two seconds, ideally. The reason for that is how people perceive things. If we click to buy a ticket and we see the ticket increased, and then within two seconds, we get a response that, hey, didn't happen and you have to revert that state back. If it happens within two seconds, users are typically, oh, okay, that didn't work out, but if it happens after that time, three, four, five seconds, in users' mind, that action's already complete, so if you have five seconds, it's like ticket, no, no, no, there's no ticket, then it's like a kind of jarring experience.
[00:04:23]
So optimistic UI updates is really powerful, really cool, feels almost instant, right? It's called perceived performance improvement, but you have to be a little bit careful with them as well. So in this case, we're going to use this, that means if we're using this optimistic update, we have to keep that state somewhere, right, before that. For our events, we're not keeping events anywhere.
[00:04:52]
We're asking for events, we're loading events, then we show them. We're deleting the event, deleting the event, then we delete event, right? If we reload the events, we create an event, it still goes in, that's successful, and then we navigate to the home page, right? So all of them were basically, we needed to have that network communication back and forth first, before we can do anything.
[00:05:22]
In this case, we want to have internal state, so our service would be stateful, it will have the state of the tickets. And this is what we're going to implement. All right, so what are we going to do here? So first of all, we'll still inject our HTTP server because that's how we're going to communicate. This is a mutation API, this is post. So we're going to inject our HTTP client service.
[00:06:00]
Then we'll have the URL that we're going to be working with, right, and this is the one that we just created the injection token for. We'll call it the tickets URL and this is what we can just inject our tickets URL injection token. And again, just backtrack a little bit, this one, you don't have to do this, right? You can do the same technique that we've done with the tokens here, basically provided in route, have a factory or just provide the value, use value immediately, right?
[00:06:47]
I've shown this way, so you are aware that you can do it this way as well. All right, so let's get to our cart service. So how are our tickets? Awesome. Now, here are these tickets, they don't care of individual tickets, right? It just needs a number. Give me the total count. So for that, it means it's what? It's a derived state. We have a source of truth, the number of actual tickets array, like tickets array, and then we have a derived value which is a count of that.
[00:07:27]
So that means we have a computed. Computed is always a derived value and the beauty of it is again, it's a read only, so nobody can really change it, but so it's safe to expose this. So let's do read only count and what this, what the source of truth for this will be, this is going to be our internal state. So we'll have our ticket IDs and again, this is the signal of string and initially you will have nothing.
[00:08:25]
So we'll have an array of all those ticket IDs. So the count will be based on that. So we're going to go, it's going to be a computed value, computed off this ticket IDs. And then we have that signal they're going to be tracking and we just need the length from this. So we have this ticket count. So again, this will be our internal state, so that's what makes the service stateful.
[00:09:03]
Now, let's see how we can operate on the state. We cannot, we can, can we use the HTTP resource? I probably can, we need to reload it every so often, but in this case, we'll just use the HTTP client directly because, again, we're not exposing this resource anywhere because it's like more of an internal source of truth for the state. So let's create the function called load tickets and in this function, we're going to be, this HTTP get, so we're going to get our ticket entries.
[00:09:58]
It's HTTP get, we get of our ticket entries and we're going to call our API URL, this one, this ticket's URL. Okay, so now we have the data. This returns us also the observable, there it is somewhere, there you go, just as observable. But in this case, we're not going to be returning this load tickets outside, so somebody else subscribed, we'll internally subscribe for this as well.
[00:10:31]
So load tickets will actually be of type void, it's not going to return anything because what we're doing here is a little indirection. We're reading the state from the tickets, in fact, just the count. That's what we're reading and we don't care who's changing the state. Are we adding tickets, loading tickets, removing tickets, whatever's happening, the count does not care, right?
[00:11:04]
So when we load tickets, we also don't care, so we'll just subscribe to this and then this subscription returns us this data, the ticket entry data. And what we're going to do is on our next, we'll have our data that is being returned and we're going to be pushing it to our ticket IDs. We can do it, IDs, we can have our data, we're going to map.
[00:11:35]
We don't really care about the specific ID of the ticket, we just need event IDs. So that's why we just have that string of the ticket event IDs. We're getting the object back, so we'll just map it to that event ID. We don't really need anything else, so now this is an array of strings and now that's the one that I'm going to set to our ticket IDs, this ticket IDs set IDs, right?
[00:12:11]
So now, when we call this function, this method, it will go to an HTTP client, it will go to this ticket API, it will bring the data back, we get the ticket back, and then it will send the tickets internally. And then the count will say, oh, this updated, this is a derived state, I have my count, right? And what we can do, for example, in this case, we can load the tickets whenever this cart service is injected, so we can almost do it immediately as well.
[00:12:49]
We can do it in the constructor here, this load tickets. In this case, it's even private, so we cannot even reload the tickets otherwise. We can make it public as well, if any of the components like, maybe it's editing ticket, maybe does something with this, right, it's like, hey, let's do something about this. At some point, you might want to reload the tickets, you can make it public.
[00:13:21]
All right, so do we have any tickets now? I don't think we have any tickets, so yeah, right now, but we see that the API is called. Let me see, that's not our stuff, we can see, is the API called? No, the API is not called, why is the API not called? You haven't clicked the button yet to buy a ticket. That's true, but we should still call the API to check if we have to, exactly.
[00:13:42]
Why is it not called? Any ideas? You haven't implemented the inject, exactly. We're not injecting the service anywhere. If we're not injecting the service anywhere, it's not constructed yet. So even if it's provided in root, it doesn't mean it's proactively constructed. It's only constructed when it's needed.
Learn Straight from the Experts Who Shape the Modern Web
- 250+In-depth Courses
- Industry Leading Experts
- 24Learning Paths
- Live Interactive Workshops