Authorization is a must thing to do in web applications nowadays. It’s like a morning coffee, you need to drink it to be capable of doing important tasks throughout a day.
There are lots of different use cases and scenarios when it comes to the Authorization, but in my frontend career I’ve seen mostly these two patterns
- a user can navigate somewhere depending on authorization e.g “User profile page”
- a user can see some elements depending on authorization e.g “Pay button”, “Sign out”…
In Angular, the first problem is already solved using Route Guards. But in the second one, there are no core concepts on how to do that. The most common solution is to add the
*ngIf="isAuthorized" directive on every element, to manage its visibility based on authorization. In order to achieve that, you need to inject some service that stores a user authorization state, assign it to the component variable e.g
isAuthorized and then add it to the
ngIf directive. By that, you are copy/paste the same code over and over again and it becomes less maintainable, bug-prone, and time-consuming. It would be really nice if we could encapsulate all the logic somewhere, and just use it inside our view. That’s when a structural directive comes into play.
A Structural Directive can be defined in many terms, but for me, the simplest definition is this: a structural directive is responsible for adding or removing an element inside the view. If we inspect all the core structural directives (
ngSwitch ), they all share the same behavior, either they add or remove some element from the view. That’s the exact behavior we need; If the user is authorized, we need to add some element to the view, otherwise the opposite. Before creating our structural directive, let’s glimpse a bit how it actually works.
How Structural Directive works
Whenever you add
*ngIf directive to the element
<button *ngIf="show"> Hello</button>
Angular unwraps this code and transforms it to the following HTML
<button> Hello </button>
if you don’t know what
ng-template is don’t worry the only thing we want to know is, whenever Angular sees
ng-template , it removes it from the DOM automatically, and you have to manually put it back inside the view. So inside the
ngIf directive, based on the condition (
show value), it will be added inside the DOM again or it will be removed. Let’s create our directive and it will be more obvious for us.
At first, let’s define our directive.
As you can see there is nothing complicated here, we’ve just defined a structural directive like an Attribute directive. If we take a closer look at the constructor, there are two entities that are passed:
ViewContainerRef which we will explore later a bit. now let’s add this directive to the random element inside our view
<button *authorize> Log Out </button>
As we know if we do that, Angular will transform this HTML to the following code
<button>Log Out </button></ng-template>
I think you already guessed, that exactly this
ng-template , is passed inside the constructor using
ViewContainerRef ? you can think about it, as some service that is capable of adding or removing something from the view. Angular injects it for us. By combining these two we can fully implement our feature. Let’s add some logic to our Authorize directive
Based on the
isAuthorized field we are adding or removing this element from the view. I know
createEmbeddedView doesn’t win the championship for the best meaningful names in the world, but just remember that
createEmbeddedView adds an element to the view, and the
clear method removes it.
Now we need to make sure, that the
isAuthorized field is not a constant, and it is fully dependent on the user's authorization. we could achieve that, using Angular’s coolest feature Dependency Injection.
Let’s create some service, called
UserStore that tracks user authorization, inject it inside our directive, and use it appropriately.
authorize directive is fully dependent on the user authorization. We are almost finished except we are facing a little quirk thing here. As we can see, whenever we use
authorize directive it will check
isAuthorized only once and then just gets silent, so if you authorize a user later, the element will be still hidden, because it is checked only once. For fixing this problem we can use our old friend Observable. let’s rewrite our
userStore with observables.
Now our authorization is not static anymore it’s a stream so it will emit new value whenever it changes. We are emitting
setTimeout , to pretend authorization.
All we have left to do is listen to these changes inside the
Now our directive is fully dynamic. Besides that, we add a little check using
hasView field, so we don’t end up calling
createEmbeddedView extra times (if authorize value fires twice with
True value, we don't want to add it twice )
We are done. we have a powerful directive that saves us tons of code and time in the future.