Hi. Everything you have to know, about the brand new resource() function, in Angular 19, you can find, in this video. In this tutorial we will build from scratch a little Angular app, where you will be able, to load data from the server.
You will be able to reload it, or change it afterwards by adding a new user to the list, or clear the list up. Besides that we will Implement a user search feature, where I will demonstrate how you can handle concurrency requests, and cancel the pending request when the new one arrives. And of course, while the data is being loaded, user will see a progress bar making the user experience much better.
And this everything we will achieve only by using the resource function and we will not write any single line of RxJS code. Sometimes you might feel that things are working like magic, but no worries, I will be diving into the source code and demystify things that might not be obvious for you. If it is a first time on my channel and you like deep dives into Angular my name is Dmytro Mezhenskyi and on my channel you can find a lot of advanced and unique Angular tutorials, that you can check out later, but now let's try to learn more about this new reactive primitive called resource().
First of all, let's try to figure out why this resource() function exist. Right after signals were introduced in Angular, a few things became very clear immediately. Signals are reactive primitives that will be used by the Angular core making their dependency on RxJS optional.
Another thing that we realized about Angular signals quite quickly, is that they're very good for synchronous reactivity, but when it comes to asynchronous code they perform not so well, and RxJS much better in those cases. And this was actually a problem in making RxJS fully optional, because we cannot simply avoid the asynchronous code, but at the same time, we don't have any signal based alternative that would nicely work with asynchronous code and could handle the most popular use cases. So that we could include RxJS only when its power is justified.
And this new reactive primitive resource(), supposed to fill this gap. So let's have a look how it looks in practice. Before we get started, I just wanted to mention that everything you will see in this video is related to Angular 19, so make sure that you updated your project to this exact version and once you have done it, you will be able to import the brand new function called resource() from the 'angular/core'.
Please also keep in mind that, in Angular 19 the resource() function, is in developer preview mode, which means, that it is not ready for production. It might change in the future, and it was exposed just to let you play with it, and provide feedback to Angular core Team. All right, so let's go and create our first resource() and I'm going to name it 'users' resource() and the resource() function itself requires a certain configuration object.
And there you can see a bunch of properties that you can configure, there is only one required, called loader, so let's implement it first. And the loader is a function that's supposed to asynchronously load the data into this 'users' resource(). And this function should return a promise, or if to be more precise it has to be something PromiseLike.
So it means that, it should not be necessary a promise, but it should be something that has an API, similar to what promise has. I will be using a normal promise that will be returned by the calling the fetch() function. Fetch() function it is the, part of the web platform API, and it allows me to perform http calls to a certain server endpoint.
And then the data, the endpoint returns, I can resolve by calling the json() method, and then the data will be available for further usage. Alternatively you can also use the 'async/await' syntax, it is absolutely okay. Just choose whatever style you like more.
I personally prefer this 'async/await' syntax, because I find it more cleaner. But whatever syntax you choose, if you look up into Chrome Dev Tools inside the network tab, you will see that the call to the users's endpoint has been executed. Which proves that the loader() function of the user resource() has been invoked.
But how can we read the data returned by the resource() loader? For that we have to take a closer look at what resource() function returns. And it returns a special data structure called ResourceRef.
And you can think of it, as a representation of the current state of a certain source, and also besides the reading the state, it provides you with a certain API, that allows you to manipulate this state and a little bit later I will show you how to do that, but so far let's try to read the current value from the user's resource(). And I'm going to do that inside the component template, there I have a dedicated list and here I'm going to use the 'For Loop' and iterate through the 'users' source values, right. So there will be the user that server returns, and I'm going to track them by using userID which is not highlighted by the way, because we don't have strict typing so far, for our source, so let's use this chance to improve it, and actually the type of resource() function is a generic that has two values.
The first value this is what resource loader returns. And in my case it is array of users, and the second parameter it is the request type. We will come to request feature a little bit later, so far let's define it as a unknown type.
All right so now typings for our 'users resource' are much stricter. And we can get back to the component template and now you can see that the available users properties are shown up, and if we make any mistake, and try to access the property that doesn't exist, we will get the compilation error. Which is actually cool.
And now inside the 'for' block I can already define a new <li> element and there let's display maybe username. Here we go. And also the no data <li> element let's show, when the users value is empty.
And there is nothing to show. And now if I save my change, and reload the application, you can see the list of users, I get from the server is rendered right on the page. But what if something goes wrong and we encounter an error during the loader() function call?
How we can handle the error use case inside the resource()? And actually it is pretty easy to do, because the ResourceRef provides a dedicated property called 'error' that contains an error, if it happens inside the loader() function, or it is undefined if everything went well. So to notify user about the error, we can go to the component template, and somewhere here, I can create dedicated block that will be visible if our users's resource() contains the error.
And there inside I'm going to display what exactly went wrong. And to test this scenario we can scroll a little bit down, and let's try maybe remove entirely the endpoint URL from the fetch() function. And in such a way, we can explicitly trigger the error.
So you can see that the error message appeared right there, exactly as I expected. And by the way, since the usage of the fetch() function in Angular becomes kind of usual thing, you have to keep in mind one important pitfall, when it comes to error catching. For example let's try to simulate the use case, when we call the endpoint that doesn't exist.
So if we look up into the console, you can see that the request failed with the status code 404, but we don't see the corresponding error message. The thing is that when the server responses with the status code like 404 or 500 the fetch function doesn't consider it as an error. So the promise returned by the fetch() function, will not be rejected.
And this is the reason why we don't see the error message there. To change this behavior you can explicitly check if the server response is not okay, in this case you simply throw an error with the corresponding message. So now if I reload the application you can see that the error message has appeared, because server responded with the status 404.
So let's very quickly revert this change, and use the real API URL, and let's move forward. And the next thing I would like to discuss with you, are resource statuses. The ResourceRef has a corresponding signal() called status, and let's try to subscribe to the changes of this signal() by using the effect() function.
And simply console. log() the values of it. So if you save this change, and open the Chrome Dev tools and go to the console tab, you can see resource statuses console.
loged and they are represented by some magical numbers. Each of these numbers represent a certain resource state like loading, reloading, resolved etc. And we can easily decode those numbers, by using a dedicated enum, called ResourceStatuses.
That you can again import from the 'angular/core' package. This enum provides all the possible resource statuses, so you can use it in your code in order to react to certain ResourceStatus change and reflect this, on the UI. For example you might create a computed property, where you just track the changes inside the resource status, and check if it is in loading state, or not.
However you should not do this for exactly loading status, because resourceRef out of the box comes with the property isLoading(), that handles exactly this use case for you, and if you have a look at the source code, and exact implementation of it, you can see that this is still the same computed(), that checks if the status is 'in loading' or 'reloading' state. So I don't need this computed() I created, and I can use isLoading() property, so I can go to component template and show some spinner or progress bar in my template and for that, I will be using the Angular Material progress-bar component, that I have to import and somewhere, somewhere here, I can create a dedicated 'if' block, where I will display the progress-bar component in case the source, the user source, is in the loading status. All right, now let's try to save our change and test if everything works.
So I'm going to reload my page, and you probably you could see the progress-bar appeared, but it happened so fast, so that's why let's open their Dev tools and try to simulate the very slow 3G network, and yeah, you could see that the progress bar was visible for a second, and then when the data was resolved, it disappeared so everything works as we expect. And now let's talk how we can manipulate the state of the resource() and what API do we have for that. First of all, we can reload our resource(), meaning that we can retrigger the loader() function and grab the latest data from the server.
It is pretty easy to do that, because the ResourceRef has a dedicated reload() method that you can call anytime, for example I have here a dedicated button reload, so I can add the click event listener to this button, and when user clicks it we can reload the resource. So now if I save my change, and try to click the reload button, you can see that my resource was reloaded, and you could see the dedicated progress-bar here at the top. We can also change the value of the resource locally at any time, and we have two options to do that.
We can set a new value based on the previous value of the resource, or we can set the new value directly. So let's have a look at them one by one. For example let's imagine that in addition to already fetched users from the server, I want to add a new one in the beginning of this list.
So in this case I would need to add another click event handler to the add user button, and the handler of it will be addUser() method. Here you can already see prepared dummy user that I'm going to add to this list, and we can add this user by using the update() method that works the same way as with normal WritableSignal(), there you have a callback, where you take as argument the previous value of the signal, or resource. And from there you return the new state, the new value for this signal, and in my case I have to also check if the previous value is not undefined, because it could be undefined, right.
And if it is the case then we return just simply array with this new user, and this is pretty much it. So we can try to click the add user button, and we see that the new user has been added in the beginning of the user list. And as I said before, we can also set the new value for the resource directly, and for that we are going to use a dedicated set() method, that again works absolutely the same as for any WriteableSignal, so when we call the clear button, let's say that we set value to the empty array, and in such a way we clear the list completely.
And now if I try to click the clear button, you can see that all users has been removed from the list. And this is cool and I personally find this pretty impressive what we learned so far, even though we didn't cover everything yet, so let's continue continue and let's have a look what other API the resource function provides. And how it can help us to implement feature like user search, so that when user types anything into the search field, we should request server with the right searching criteria, and get back users, name of which ones starts with the search string.
For that, we would need to create another signal() inside our user-search component, and the value of it will be an empty string by default. Also we have to bind this signal() with the corresponding search-input and for that let's go to the component template, find this input and there I'm going to attach the input event listener, and once user enters a new value, we will take it from the event. target and set as a new value, for the corresponding query signal().
You can see currently an issue with typings and I'm going to fix it the way you should never use in your production code, and I'm going to cast it to any, inside template, just to avoid creating a separate method, only for target type casting. I hope you will forgive me my laziness. Anyway now the question is how we can wire up this new query signal() with our users's resource.
Because if you try to use the query signal() directly inside the loader() function it will not work. And the loader() function will not be re-triggered every time when query signal() changes. And to solve this issue resource has another property called 'request'.
Where we define a function that returns a signal() or group of signals() changes of which one will call the re-execution of the loader() functions, and their new values will be provided to the loader() function as an argument. So you can destructure the loader config, and take the property request from there, that contains the value the request function returns. And then you can use it anywhere inside your loader() function.
And if we save this change, and try to type any string into the user search, you can see that every time the search query changes, we re-execute the HTTP call, with a proper name like parameter, and get the proper values from backend. If your loader() function depends on values from multiple signals(), then you can return from the request() function, not a single signal(), but you can return an object of signals(). So in my case it could be an object that has property 'query' and the value of it is a query signal().
And then I could access this query, by using again the request object, and they're just using the query key. Again, you can see some typings issues this is because earlier in this video we defined the request type as an unknown, but now we know what the type is, and this is the object with a key 'query' and the value type of string. So we can define it here, and the error has disappeared.
All right now let's talk about concurrency request. For example what will happen if I fire a new HTTP call, while the previous one is pending? And as you can see by default, all those requests will be executed, which might remind you the behavior of the RxJS operator called mergeMap().
This however might not be the behavior you expect. And you would like to cancel the pending request when the new one arrives, and in this case you have to utilize a thing, called abortSignal(). That you can destructure from the loader() function argument, and use it in order to abort asynchronous operations like the fetch() function call.
You simply have to provide an additional config for it, and there for the parameter signal() you provide the AbortSignal. And after that, when you save this change, and try to perform multiple HTTP calls in a row, you can see that the pending request has been cancelled, and completely executed only the latest request. Which is actually very similar to the behavior of the switchMap() RxJS operator.
I'm pretty sure that for many of you, this everything looks like magic, and it brings a lot of confusion, like 'What this AbortSignal is? How it prevents the fetch call etc? '.
So let me take another couple of minutes, to demystify all this things for you. The first thing you have to understand, is that the AbortSignal as well as the signal() property in the fetch() function config. They have nothing to do with Angular signals().
This is completely different concept. The AbortSignal allows you to abort execution of asynchronous operations in JavaScript. And it is a part of AbortController class, that is built-in into JavaScript, so you can instantiate your own instance, of the AbortController, and the AbortSignal from this class instance, you can provide instead of the AbortSignal I received in the reload() function.
And whenever I call the abort() method of my custom AbortController, it will notify the fetch() function to cancel these asynchronous operations. We could even wrap this abort() method call in some setTimeout() and call it after, I don't know 500 milliseconds, and define the reason that, the request takes too much time, a bit silly example, yeah. But it demonstrates the idea how it works.
So now if the request takes more than 500 milliseconds, then we will see an error, because after 500 milliseconds, the abort() method involved, and it triggers the AbortSignal, that eventually cancels the HTTP request. So as you can see there is no magic here at all. And under the hood of the resource() function happens pretty much the same thing, and we can make sure it by diving into the source code of the resource() function.
All right, so this is the source code of the resource() function and what it does, it returns a new instance of the WritableResourceImplementation class, which is right here, and you can see it has a private property called pendingController, that's supposed to be an instance of the AbortController. And here's a method called loadEffect() and this is the method that actually involves our loader() function, right. And somewhere here, there is new instance of the AbortController is created, and from there, they structured the signal() property.
Renamed to the AbortSignal and it is provided as an argument for the loader() function. This is exactly this function and this is exactly that AbortSignal and then, at some point, the abort() method of the AbortController is called which fires the AbortSignal. We can, by the way, have a look when it actually happens.
So let's try to find where the abort() is called, so it is inside, okay, this is the private method that actually calls the abort() method, and we can have a look at which places it was called. So the first place this is on local value, and this method is executed when we locally update the value of the resource. For example when we call set() method or update(), so in this case the AbortSignal will be fired, then it happens when the writable resource is destroyed.
Which is pretty obvious. And then it happens also you can see, in the beginning of the calling loadEffect() method, and exactly this causes this switchMap() effect when we use the AbortSignal with our fetch() function. You might be also curious 'why do we get an error when we abort the request by using our custom AbortController and why doesn't happen when we use the AbortController that we destructure from the loader() function?
'. And this is because under the hood the function is called inside a try/catch block, and when the error goes to the catch block. Here happens the check if the operation caused by abortion, then happens simple return, and we simply do not reach this step, when the error status is setup, and by the way, this is something you could Implement in your custom abortion logic.
If you need it of course. But I must also say that I'm not sure that this will be the recommended way to implement the custom abortions logic. It is just too early state to say something certain, maybe in the future we will see a dedicated API, I don't know, I did it just to demonstrate what is hidden behind the AbortSignal, so it doesn't look like a magic for you.
And if you don't mind I will just revert things back, a little bit. So far this was everything I would like to mention about the resource() function, and please remember that this is still an experimental feature. And I think it's just the beginning of the new entire massive Angular chapter, and later we will see more features and more patterns built on top of this reactive primitive.
But maybe you have different opinion, so let's discuss it in the comment section below, or you can join our cozy Discord community of more than 1,000 Angular developers, around the world. And we are looking forward meeting exactly you, as well. If you like the way how I explain stuff by diving deep into the Angular source code, and if you would like to support the release of new such videos, right here, you can see all available options how you could do that, and whatever option you choose I am so grateful for that, and I appreciate your support a lot.