Writing your first custom structural directive
In this recipe, you’ll write your first custom structural directive named showFor (or *appShowFor with the prefix). A structural directive is one that can add or remove elements from the DOM. So, with this directive, we will add the particular element to the DOM if a provided Boolean is true, and we will remove it after the specified time (provided as a number representing milliseconds).
Getting ready
The app that we are going to work with resides in start/apps/chapter02/ng-show-for-directive inside the cloned repository:
- Open the code repository in your code editor.
- Open the terminal, navigate to the code repository directory, and run the following command to serve the project:
npm run serve ng-show-for-directiveThis should open the app in a new browser tab, and you should see the following:

Figure 2.7: ng-show-for-directive app running on http://localhost:4200
How to do it…
- First of all, we’ll create a directive using the following command in the workspace root folder:
cd start && nx g directive show-for --directory apps/chapter02/ng-show-for-directive/src/app/directives --standalone=falseIf asked, choose the
@nx/angular:component schematicsand choose the “As provided” action.
- Now, instead of the
*ngIfdirective in theapp.component.htmlfile on the element with the class"dialog", we can use our*appShowFordirective:... <main class="content" role="main"> <button (click)="toggleDialog()">Toggle Dialog</button> <div class="dialog" *appShowFor="showDialog"> <div class="dialog__heading">...</div> <div class="dialog__body">...</div> </div> </main> - Now that we have set the condition, we need to create two
@Inputproperties inside the directive’s TypeScript file, one being abooleanproperty and one being anumber. We’ll use asetterto intercept the Boolean value’s changes and will log the value to the console for now:import { Directive, Input } from '@angular/core'; @Directive({ selector: '[appShowFor]', }) export class ShowForDirective { @Input() duration = 1500; @Input() set appShowFor(value: boolean) { console.log({ showForValue: value }); } } - If you tap on the Toggle Dialog button now, you should see the values being changed and reflected on the console, as follows:

Figure 2.8: Console logs displaying changes for the appShowFor directive values
- Now, we’re moving toward the actual implementation of showing and hiding the content based on the value being
falseandtruerespectively. For that, we first need theTemplateRefservice and theViewContainerRefservice injected into the constructor of theif-not.directive.tsfile. Let’s add these, as follows:import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; @Directive({ selector: '[appShowFor]' }) export class ShowForDirective{ @Input() duration = 1500; @Input() set appShowFor(value: boolean) { console.log({ showForValue: value }); } constructor( private templateRef: TemplateRef<any>, private viewContainerRef: ViewContainerRef ) {} } - Now let’s show the element. We’re going to create a
showmethod and we’ll call it when the value of theappShowForproperty becomestrue. The code should look as follows:... export class ShowForDirective { @Input() duration = 1500; @Input() set appShowFor(value: boolean) { console.log({ showForValue: value }); if (value) { this.show(); } } show() { this.viewContainerRef.createEmbeddedView( this.templateRef ); } constructor(...) {} }If you click the Toggle Dialog button now, you should be able to see the dialog as follows:

Figure 2.9: Dialog being shown using the show method
- Let’s implement the logic of hiding the dialog. We’ll use an
@Output()prop with anEventEmitterfor this as we want the value ofappShowForthat’s passed by the parent to be updated, instead of updating it within the directive. Modify the code as follows:import { ... , EventEmitter } from '@angular/core'; ... export class ShowForDirective { @Input() duration = 1500; @Input() set appShowFor(value: boolean) { ... } @Output() elementHidden = new EventEmitter(); show() {...} hide() { this.viewContainerRef.clear(); } constructor(...) {} } - Now that we have the
hidemethod there, let’s call it after the duration time saved in thedurationproperty of the directive. This is so the dialog hides after that duration. Modify the code of theshowmethod as follows:show() { this.viewContainerRef.createEmbeddedView( this.templateRef ); setTimeout(() => { this.elementHidden.emit(); }, this.duration); }With this change, you’ll see that nothing happens if you click the Toggle Dialog button after the dialog is shown, i.e., it never gets hidden. For that, we need to listen to the
elementHiddenevent emitter we just created.
- Let’s make the
app.component.htmllisten to theelementHiddenevent listener to change the value of theshowDialogproperty as follows:<div class="dialog" *appShowFor="showDialog" (elementHidden)="toggleDialog()"> <div class="dialog__heading"> I am a Dialog </div> <div class="dialog__body"> And this is some random content </div> </div>With this change, you’ll notice that it still doesn’t work. Yep! Because we need to call the
hidemethod when the value ofshowDialogpassed as theappShowForprop is set tofalse.
- Let’s call the
hidemethod in theShowForDirective(in theappShowForproperty’ssetmethod) when the value ofappShowForbecomesfalseas follows:@Input() set appShowFor(value: boolean) { console.log({ showForValue: value }); if (value) { this.show(); } else { this.hide(); } }The thing is… this still won’t work because a structural directive in Angular can’t emit values. Or even if it does, the parent element won’t be able to listen to it. The following Stack Overflow question discusses why and links to an open GitHub issue in the Angular repository as well: https://stackoverflow.com/q/44235638.
- To make our structural directive work, we need to get rid of the syntactic sugar it comes with. Let’s modify the
app.component.htmlto use the directive in a different (expanded) way, as follows:<main class="content" role="main"> <button (click)="toggleDialog()">Toggle Dialog</button> <ng-template [appShowFor]="showDialog" (elementHidden)="toggleDialog()"> <div class="dialog"> <div class="dialog__heading"> I am a Dialog </div> <div class="dialog__body"> And this is some random content </div> </div> </ng-template> </main>The dialog should be hidden now. Yay! But wait. Try clicking the Toggle Dialog button lots of times quickly. You’ll see that the app goes crazy. That’s because we end up having too many
setTimeoutfunctions registered.
- Let’s clear the
setTimeoutif we toggle the dialog to manually hide it. Update the code for theShowForDirectiveclass as follows:... export class ShowForDirective { ... timer!: ReturnType<typeof setTimeout>; show() { this.viewContainerRef.createEmbeddedView( this.templateRef ); this.timer = setTimeout(() => { this.elementHidden.emit(); }, this.duration); } hide() { clearTimeout(this.timer); this.viewContainerRef.clear(); } constructor(...) {} }
Awesome! You’ll notice that even if you click the Toggle Dialog button fast and too many times, the app behaves correctly.
How it works…
Structural directives in Angular are special for multiple reasons. First, they allow you to manipulate DOM elements—that is, not just showing and hiding but also adding and removing elements entirely from the DOM based on your needs. Moreover, they have the * prefix, which binds to all the magic Angular does behind the scenes. For example, Angular automatically provides the TemplateRef and ViewContainer for working with this directive. As an example, *ngIf and *ngFor are both structural directives that work behind the scenes with the <ng-template> directive containing the content you bind the directive to. They then create the required variables/properties for you in the scope of ng-template. In this recipe, we do the same. We use the TemplateRef service to access the <ng-template> directive that Angular creates for us behind the scenes, containing the host element to which our appShowFor directive is applied. We use the ViewContainerRef service to add the TemplateRef to the DOM via the createEmbeddedView method.
We do this when the value of the appShowFor property becomes true. Notice that we’re intercepting the property appShowFor using a setter. We learned about this in Chapter 1, Winning Components Communication. We then use a setTimeout to automatically notify the parent component that the value passed to the appShowFor property needs to be changed to false. We do this using an @Output() emitter named elementHidden. Notice that we’re not supposed to make it false within the directive. The parent component is supposed to do it and it will automatically reflect in the directive. Our directive is supposed to react to that change and hide (or remove) the TemplateRef from the ViewContainer. You can see that we do this in the hide method using the this.viewContainerRef.clear(); statement. One of the key things to learn from this recipe is that if we use syntactic sugar, i.e., *appShowFor, in the app.component.html, we can’t listen to the elementHidden event emitter. That’s because this is a quirk of Angular - there’s an open issue on GitHub about this (check the See also section). For this to work, we removed the syntactic sugar and expanded the syntax by using a <ng-template> to wrap our dialog’s HTML in step 11. Notice that we just used [appShowFor] to pass the showDialog variable instead of *appShowFor="showDialog". And we are also listening to the elementHidden event on the <ng-template> element itself.
See also
- Angular structural directive microsyntax documentation: https://angular.io/guide/structural-directives#microsyntax
- Angular structural directives documentation: https://angular.io/guide/structural-directives
- Creating a Structural Directive by Rangle.io: https://angular-2-training-book.rangle.io/advanced-angular/directives/creating_a_structural_directive
- Sugar (*) syntax does not support @Output (and exportAs): https://github.com/angular/angular/issues/12121