Structural directives allow us to change the DOM In different ways: By adding, changing, or removing all the elements on the page. They are added as an attribute in the HTML element. The directive should always be prefixed with an * to make them easy to distinguish. Angular provides some built-in directives such as ngIf, ngFor, etc.
💡 The asterisk (*) is syntactic sugar that wraps under the hood of our HTML, where we attach this directive to the ng-template.
Let’s Create Our First Directive
We will create a directive that shows some content after a period of time.
First, you need to run this command on the Angular CLI to create a directive class:
/* ng generate directive name-of-directive */
ng generate directive showContentAfterDelay
Add the following code on the directive class generated:
import { Directive, Input, OnInit, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[ozarksShowContent]'
})
export class ShowContentDirective implements OnInit {
@Input('ozarksShowContent') delay = 0
constructor(private viewContainerRef: ViewContainerRef,
private template: TemplateRef<any>)
{ }
ngOnInit(): void {
this.viewContainerRef.createEmbeddedView(this.template);
setTimeout(() => {
// cleans up the view cointainer ref
this.viewContainerRef.clear();
}, this.delay)
}
}
The Directive provides the functionality of the @Directive decorator in which we give its property selector to ozarksShowContent so that we can use this selector anywhere in the application.
We pass data to the directive using the @Input() decorator - it's a property that the parent component (Parent) passes to the child component.
We inject the ViewCointainerRef and TemplateRef in the constructor. The ViewContainerRef represents a container where one or more views can be attached, while the TemplateRef is a way to reference the ng-template in the component/directive.
On the template, we add our custom directive like this:
<h1>Welcome to the Lazy-O Motel</h1>
<section *ozarksShowContent="5000">
<h1>Let Me See You!</h1></section>
If you try to bind the input value to the template like this it won’t work because Angular tries to resolve the input as a property of the section which does not exist.
<!--This will not work-->
<h1>Welcome to the Lazy-O Motel</h1>
<section *ozarksShowContent [delay]="5000">
<h1>Let Me See You!</h1>
</section>
In case you would like to render content after the 5 seconds, this is how you implement it:
On the .ts file
import { Directive, Input, OnInit, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[ozarksShowContent]'
})
export class ShowContentDirective implements OnInit {
@Input('ozarksShowContent') delay = 0
// showing content after some time
@Input("ozarksShowContentThen") placeholder: TemplateRef<any> = null;
constructor(private viewContainerRef: ViewContainerRef,
private template: TemplateRef<any>)
{ }
ngOnInit(): void {
this.viewContainerRef.createEmbeddedView(this.template);
setTimeout(() => {
// cleans up the view cointainer ref
this.viewContainerRef.clear();
if(this.placeholder){
// if the value is true,show the template
this.viewContainerRef.createEmbeddedView(this.placeholder )
}
}, this.delay)
}
}
On the .html file
<section *ozarksShowContent="5000; then placeholder">
<h1>Let Me See You!</h1>
</section>
<ng-template #placeholder>
<mat-toolbar color="primary">
<mat-icon>hotel</mat-icon>
<span>Welcome to the Lazy-O Motel </span>
</mat-toolbar>
</ng-template>
Conclusion
Custom directives can be helpful when custom logic is involved in the DOM element. For example, in user role management or you may want to show some aspects to different customers, say you have premium customers, free-plan customers, etc., on a web application. When inbuilt directives don't give the required output, we can always write a custom directive.
Happy Learning!
Top comments (0)