Video details

Structural Directives in Angular – How to Make It Strict (In-Depth)

Angular
01.31.2022
English

#angular #structuraldirectives #webdevelopment #tutorial Structural Directives are one of the most powerful features in Angular and this is the 3rd episode of the video series about "Custom Structural Directives. From this tutorial you will learn how to make types in your directives more strict, so you will leave less space for potential errors which otherwise might be caught too late when the user already interacts with your App in production. I hope you will learn a lot of new tricks today. Let me know if you like the video!
💥 Angular courses made by Dmytro - https://bit.ly/df-courses 💥 ✂️ Use coupon YOUTUBE_DISCOUNT to get a 15%-off discount ✂️
🕒 Time Codes: 00:00:00 - Intro; 00:01:22 - Define strict type for Directive (Template) context; 00:03:15 - Implement Context Type Guard; 00:05:10 - Explenation of what is Type Predicate in TypeScript; 00:07:16 - Continue with Context Type Guard; 00:08:17 - Introducing Directive Input Type Narrowing; 00:10:05 - Implementing Directive for the demo use-case; 00:14:12 - Explanation of Input Type Narrowing; 00:17:11 - Outro;
🔗
Link to GitHub repo: https://github.com/DMezhenskyi/structural-directives-example/tree/strict-directives-tutorial
🔗
Link to TypeScript's Type Predicate https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates
🔗 Angular Docs about type narrowing in Structural Directives https://angular.io/guide/structural-directives#making-in-template-type-requirements-more-specific-with-template-guards

Transcript

Hi everyone. Welcome to the third episode of video series dedicated to structural directives in Angular. In the previous two video series, we covered how to work with structural directives directive inputs using the special micro syntax and how to work with the template context. And I would highly recommend you checking those videos first if you have a little experience with these kind of directives. Because in this video we're going to talk about more advanced stuff and namely we will try to make typing for our directive more strict. So eventually it will help us to catch errors even before we run our code in the browser. Also, please use timecodes in the video description in order to navigate through the video because there might be some coding parts or explanations that might be boring for experienced developers, but they are absolutely necessary for those who are less experienced and need some additional explanation. My name is Mitch Romzanski. You are watching Decoded Frontend channel. So let's get started right now. All right, so this is our super cool directive which hides some piece of content after some period of time. And I would suggest you to start with something really easy but still advanced. And mainly I would like to replace any right here for template refs to something really useful and namely inside a template ref. In this generic we define the context because as you know every template has its own context. We learned it from the previous video. So if you need whatever reason to work with the context as example, you created embedded view and then you want to go to the context. Currently there is no IntelliSense, no suggestions because the type is any. We don't know what is inside this property, so it is quite easy to fix. We just have to copy the class name and replace our any with the hide after context. And now if we save and if we try to access the context right now, we can see that we have suggestions regarding the properties which are available for our context. We can see all of them right here. So this way you can achieve better typing in your structural directive in your context. So this is the really easy part and let's move forward and I'm going to show you one interesting use case. So if we go to our template and you can see that I'm using the counter here. Yeah, I introduced variable inside my template counter and assign the counter which comes from the context. Right. But what if I did some type of here? If I save it, you can see that our directive works not as we expect. We don't have a counter here. And Angular compiler doesn't tell me that I made a typo. So it would be great to have some guard which could catch this case and indicate that we had a typo here or we're trying to access context variable which doesn't exist. And in order to achieve this we have to introduce a new static method in our directive class and namely this method called ng template contextguard. And this method takes two arguments. The first one is directive itself, it's going to be our height after directive. So I paste it here and the second argument is the context which is going to be unknown. Then we have to do the next trick. I will write it and then I will explain. So it will be easier to understand. So we should say in return type that context is our hide after context and then we say just return true. What's going on here? So it is so called type predicate in TypeScript. And if you know what is that, you can use timecodes in the video description and skip this section. For those who see first time this magic, I will try to quickly explain how it works. So we use the type predicate if we want to check if something belongs to some certain type. As an example. As an example we have some function called is NUM whatever which takes the object which has a Union type. So it might be either string or it might be a number. And imagine that in runtime we have to check somehow which type this object is. So first of all what we have to do is to do some simple check and return true or false. So in our case we have to check if a type of object is number then we should tell TypeScript that object is a number. So that's why we as a return type defined type predicate and say that the object. So basically this object is number. So now we could have something like constant object which is either string or number. And then we might have some check like if is object a number. And in this case TypeScript notes that if this function returns a true it means that the object is a number. So we can safely call some method which belongs only to number. So like that. And this is how it works. And this is exactly what we do with our ng template context guard. Basically we return true which means that our context is going to be hide after context. You can do more complicated check actually if you need, but in our case we always have hide after context. So this construction is absolutely fine. In our case we can save it and now we can see already the error which throws Angular compiler to us because it says that all right, the property counter does not exist. Did you mean counter? Of course I meant counter and now if I save it we see that everything works fine again and directive works properly. It is already super cool and there are a lot of use cases where you can utilize the type guard for your context. But I would like to show you one more cool thing which you will be using probably not so often. This is really quite rare cases when you would need it, but still I will try to explain it to cover this topic about directives completely and I'm talking about narrowing type for our inputs in structural directives. I was thinking a lot what kind of feature I could introduce for this directive in order to demonstrate you this use case I gave up. I could not come up with some meaningful idea. So I decided to take the use case from official documentation because that use case was perfect fit for this type narrowing. However, the problem with this example which you can find on official Angular docs and I will drop the link in the video description to this part. The problem is that this section probably is not finished yet. I suppose because this code is I would say this is upsell code. It's not something you can copy and just paste and it will work so you have to adjust it a little bit and we'll do it right now. And once we make it work it will be pretty much clear what does this type narrowing for inputs and in which case you could use it. So I go back and I will generate a new directive. So call it if loaded. Here we have this directive and now let's maybe copy it piece by piece. So first of all let's copy and paste our types. And here is the first problem that we define data as a generic. But in order to use this generic we have to declare it right here. So this is the first problem. Our Loading stage should be also generic and here as well. All right, we are done with types then we have to import then we have to import also interface person. Let's drop it also somewhere here. And we also need to copy the content of the directive class and this component we don't need actually. So paste it here we have to import input and we have to make our Loading state also generic. So let's add it here and we have to declare this generic for the class and for the class. We can define it right after the class name like that. And here we also have to use generics. So I will replace it here and we have to also declare it for static method right there. Cool. We have figured out with types and now we have to actually bring some logic to this directive and logic will be very simple. Once we have Loading type we don't render template. Once we load it we show some template. So let's inject view container, ref and template and inside our input we will do very simple check that if state type equal to loaded we going to create embedded view and render this template. Otherwise we should clear our view as it says that. And now we have to apply this directive inside our app component. So somewhere here I will create a new section with username and there will be some data name, right? And we are going to apply. And this if loaded directive takes some Loading state. So let's create it here. It's a Loading state, it is generic. And let's say that we are Loading person, the person which comes from here, this interface, by default it's going to be type Loading, right? And then maybe somewhere here we set timeout, we assign to the data some fake data. We say that type is going to be loaded and data is some user. Let's use the name of my sweet wife and we have to provide this state as an argument for our if loaded. So pretty simple. We have a state which might be either object which has loaded type loaded and some data, or it can be the object which has only type and equal to Loading. And the argument for if loaded input could be either this or that. We're using Union type right here. And you can see right now that we have an error which says that property data doesn't exist on type Loading. And indeed our state might be either loaded which has this data type, or it might be type Loading which doesn't have this data property. But we know according to our logic which we defined here that when we generate the template, the type of if loaded means the type of this part will be always loaded. Yeah, we check it here and we create the view only if it's loaded. So we can give a hint to Angular Compiler that if we use our state inside this section where this if loaded directive is applied, then consider state of type loaded. And in order to achieve this, we have to use another static method which has the following naming convention. It starts with ng template, guard and underscore, and then there should be the name of the input. And you may wondering why it doesn't work. So we have this static method. The problem is that there is a small mistake in official documentation page right here. The problem is that if you use construction like this, the second part of this static method name should match not the setter name, but rather the input name. So you have to replace it like that. And now if we save it, you can see that our error from the template is gone. And Angular Compiler tweets the state as a loaded always because we gave a hint to it that inside the template, please consider that the type of state will be always kind of loaded, right? All right, guys, that was it. Thanks for watching until the end. I really hope that you learned something new today. Also, don't forget to leave your thoughts about the video in the comment section. And if everything what I'm doing, my videos, everything. If it makes sense to you, and if you learn a lot from it, you can always support this channel and you can also buy one of my video courses such a way you can support my channel and you can get some really cool knowledge about angular material theming and how to properly manage it. Knowledge which can help you to prepare for upcoming angular interview or you can learn how to spin up GraphQL backend literally in few minutes and I'm not joking this is really true. I will drop links to my video courses in the video description. There will be also coupon codes. Please use them and I wish you protect your week ahead. Stay safe and see you in the next video.