Video details

CanMatch Guard in Angular 14.1 Router (2022)

Angular
09.26.2022
English

Angular 14.1 comes with many useful features and in this video, we will have a look at one of them. Here I mean a brand-new CanMatch guard in Angular router that can help you to 'skip' routes depending on some certain conditions. This is quite different compared to already existing guards and brings even more flexibility to your Angular apps. I hope you will find this video useful :)
💥
Angular courses made by Dmytro - https://bit.ly/df-courses 💥 ✂️
Use coupon YOUTUBE_DISCOUNT to get a 10%-off discount ✂️
🕒
Time Codes: 00:00:00 - Intro; 00:00:36 - Router navigation lifecycle; 00:04:55 - Project overview; 00:06:20 - canMatch router guard in action; 00:14:00 - Note 1 - How to redirect from canMatch guard; 00:15:00 - Note 2 - canMatch as an replacement for canLoad; 00:16:57 - Outro;
🔗 Source code on GitHub (init state on the master branch): https://github.com/DMezhenskyi/angular-can-match-guard
💡 Short Frontend Snacks (Tips) every week here:
Twitter - https://twitter.com/DecodedFrontend Instagram - https://www.instagram.com/decodedfrontend LinkedIn - https://www.linkedin.com/in/dmezhenskyi

Transcript

Hi everyone. Welcome. My name is Metro Mothanski and in this video we will talk about a brand new angular rotor guard which is available since angular 14 one and it it is called Can Match. We will see which problems does it solve and we will learn some small but really cool angular rotor features that are available for us since the latest angular version. So stay with me and let's get started. Before we get started with coding, let's quickly visualize the main key points in the roll to navigation lifecycle. Some steps I will intentionally drop for simplification. So don't blame me and let's focus on things that are matters for this tutorial only. All right, let's think about what would we need to do first when we want to navigate to some target road. Well, first of all, we would need to find the target rotor config that matches the URL we are going to navigate. Right? So let's name it rode matching Process. I don't know if there may be some official naming for that, but in my terminology, let it be Rot Matching Process. After that, we have to resolve the config for the role we found in order to know which component needs to be mounted in the router outlet and so forth. In most of the cases, the rolled config is immediately available, so angler does nothing additional there. But if we detect that disconfig belongs to Lazy Loaded model, we have to load the model first. Right? So let's name the step, the routeconfig loading stage or Process. Once we loaded the lazy module, we have everything we need to finally recognize the road where we were supposed to navigate. So let's name this step the Road Recognizing Process. After that, we can start creating the router state snapshot. Or better to say that we do route activation. And this process we can split into smaller steps. First, we have to resolve data for the road if we need that, of course. Right. And we have to activate the component and mount it into the role rotor outlet. Correct. Good. So we got the following stages existing in the rotor navigation lifecycle. And again, it is a very simplified model of what is going on. In reality, it is a bit more complex process. But anyway, let's proceed. So, some of these stages are protected by appropriate guards in angular guards are special classes or functions that define if you are allowed to move to the next stage depending on some condition and logic that you have to define. So the Rode config loading stage has a dedicated can load guard that defines if the lazy model can be loaded from another hand. The activation stage is protected by a set of guards. First of all, there will be called a Can Deactivate Guard for the current active road to make sure that we can leave the current location. Right. And then we run Can Activate Child and can activate guards against the target road means the road where we're going to navigate. And what about the brand new guard? Well, this is what can match guard targets. It is being run during the road matching process. And basically this card makes the road, say recognizable or not for the angular router. You can think about that also as enabling or disabling the route, or making it hidden or visible if you want, or skippable this. Everything sounds cool, right? But it might be not immediately clear which exactly use cases it solves. So that's why let's quickly jump to the code. Here I have a demo application that has three simple pages the Homepage Dashboard and Not Found page. By the way, those two asterisks it is a special syntax for angular router that says that this is a fallback road if the target one was not found. And obviously, I have corresponding components to all three roles, like Home component for the root path, user Dashboard component for the Dashboard route, and the same for the Not Found route as well. And this is how my application looks in the browser. You can see currently I am on the Home page. This page has a single button that leads me to the User Dashboard, and from there I can go back to the home page. So, pretty simple one. Also, if you want, you can clone the initial application state from GitHub. I will drop the link to the repository in the video description so you can check it out later. Now, when I'm learning something new, I like to start with the smallest experiments and observe how my small changes impact the final behavior of my code. So let's do the same learning can Match guard as well. As I said, this feature is available since angular 14 one. So if your application is updated to this version or newer, your role config should have a corresponding property called Can Match where you can provide an array of Can Match guards. Likewise, any other angular rotor guard that can match one can be a simple service that implements an appropriate Can Match interface and implements logic inside that Can Match method. Then you could provide this class inside this array just like that. But I'm excited to share with you a new angular feature that comes with the angular 14.2. And this feature, sorry, allows you to provide router guards as a plane functions just like that. However, currently TypeScript complaints because the return type of this card is void, but there should be either boolean or URL tree, or it can be an observable or promise that returns one of those types. So I can return just false from it for now and let's see what will happen. So I navigate to the dashboard and I ended up on the not found page. So it looks like that by returning falls from this guard, I kind of make it invisible for angular rotor. And that's why it behaves as if this rode doesn't exist at all. And this is the reason why the not found road was eventually involved. This is very different behavior from an example can activate guard that is being involved after the road has been recognized. So if you use Connectivate instead, you will not end up on the note found page, because the road exists, but it cannot be activated, which is a noticeable difference. I hope it's clear to you. And yeah, let's just revert these changes. Okay, but which opportunities to this new behavior and this new guard open for us? This is a great question. So let's imagine the following use case. I have two types of dashboards, one for the user and the second one for admins. But I don't want to create two separate roles like Dashboard and Dashboard admin, for instance, and do something like that. Instead, I want to have one path called Dashboard. And depending on certain conditions, I want to load one of these components. Currently, if I save it as it is, it won't really work because the angular rotor uses the first match win strategy, which means that they will be activated, the first found rolled and the rest will be ignored. That's why you see that the user dashboard component has been rendered. And if we replace the order, you can see that in this case, the admin dashboard component has been activated in older angular versions. If you had to render conditionally components for one thing rode path, you would need to create some other component. And there inside you could already check the conditions and render appropriate components using the energy if directive or do some similar stuff. But having that can match guard, we can do it more elegant way, so to say. Now I can disable or enable certain routes depending on some certain conditions, so I can disable the role for the user admin Dashboard. And then angular will pick the next row that matches the Dashboard path, right? And then it will render the user Dashboard component instead of the admin one. Cool, isn't it? But of course, you won't be using static values as I do right now. Usually you want to perform some meaningful logic there, depending on some data. This data you can get from a couple of sources. First, the can match card function or method. It is being called with two parameters the route itself and the angular and the array of URL segments. And if for whatever reason you need this data to perform your logic, you can use it inside this card. But it might be not enough. And most probably you would like to inject some angular service, right? And it is clear how to do when your guard is actually the angular service. But what if you use a function as I do? In this case, you can utilize an inject function that is available since angular 14 and allows you to inject services tokens etc. And by the way, I have a dedicated video about this function and you can check it out later, there will pop up the hint. So I'm going to inject an appropriate service that is responsible for admin user permissions. And this injected service has a property is admin which is observable of boolean. So I returned that and depending if it returns true or false, there will be rendered either admin dashboard or user one. And actually this whole notation could be simplified and you could do something like that, which is cool and looks cleaner to me, but feel free to follow your own style. So we have implemented the feature we wanted to have and if the user is admin, we see that the admin dashboard has been rendered. But if I go and change the return value to false, we should see that they have been rendered the user dashboard and it is because we return false and that's why the third row cannot be matched. So Angular picks the next row that matches the path dashboard as it is defined in the URL right there. In general, this is what this card is about. However, there are a couple of remaining notes that I would like to cover in the rest of this video. But before we continue, it is a great time to support this video with your thumbs up. If you like this video, of course, and subscribe to my channel and all my other social media profiles to get even more high quality content about front end development. But let's move forward and yeah, the note number one, you can actually do redirection straight from this guard by returning the URL tree. For instance, if I suddenly decided to redirect the user without permission rights, without admin permission rights to the home page, I could do something like that. I can inject the router into the guard function using already known function inject and then because each admin is the stream, I could use the map operator allowing them role to be matched if the value is true, right? And if it is false, I build and return the URL. Three, that would get the Angular router to redirect the user to the home page. And this is basically it. You can see it works fine. The second note is that can match is a powerful replacement for the can load guard. And according to the pull request for can match guard, the can load one can be deprecated in the future in favor of can match. This is because can load has a couple of drawbacks. Firstly, Can Lord is called only when you laser load module but not when you laser load a standalone component. Surprised, right? For me it was also not so obvious. So if you want to make your component stand alone just like that, let's do it. And then you transform this route and make this component lazy by using the load component property that return this function something like that, right? And if you have here can load instead of can match and then you get an error which says that okay, I have to remove this component from the model declarations right? Because it is a standalone component right now and now you can see that my navigation was not prevented because Ken lode wasn't even called but when I reverted to can match you can see that it doesn't allow me to navigate to that page anymore and as a side effect. It doesn't load the lazy component so it is a bit more universal solution that works for lazy components and models as well. All right guys, we are done for today so please let me know in the comment section if you find this video useful and if so, please share it with your colleagues and friends in your social media. If you want to additionally support my channel, you can buy me some cup of coffee using Super Sanks button. It is located somewhere under this video so such a way you can donate some little amount of money or you can consider one of my video courses so such a way you can and support my and get some cool knowledge in return. Anyway, I wish you we had stay safe and see you in the next video.