Introduction
Angular comes with some built-in directives.
Structural Directive can be used to manipulate the HTML structure in the DOM. Using them, we can change the structure of part of the DOM.
- *ngIf
- *ngForOf
- *ngSwitch
Creating a custom structural directive
Why custom structural directive?
We have build in structural directive that satisfies our solution, what if we might come across a case that the ones provided with the framework don't solve. so here comes the custom structural directive.
So, in this article we will try cloning the *ngIf
structural directive
Let's understand how we can create *ngIf
structural directive
Let's create a project using Angular CLI
// use this command to create new angular project
ng new project-name
// create a module
ng generate module custom-if
Now let's create a custom directive
// create a custom directive
ng generate directive
generated directive should look like this
import { Directive } from '@angular/core';
@Directive({
selector: '[customIf]',
})
export class customIfDirective {
constructor() {}
}
Let's implement the basic functionality of displaying the content if passed value is true
<h2 *customIf="true">My visible content</h2>
<h2 *customIf="false">My hidden content</h2>
To achieve that, we need a couple of elements:
an input that will determine whether to show or hide the content (@Input)
a reference to the template that we want to conditionally display (TemplateRef)
a container that will provide us with access to Angular's view (ViewContainerRef)
The @input
can be just a regular class field decorators property with Angular's. For it to work as it does in the example code shown*customIf="true"
, we need to name the property the same as the attribute's selector:
import { Directive, Input } from '@angular/core';
@Directive({
selector: '[customIf]',
})
export class IfDirective {
@Input() set customIf(show: boolean) {
//code goes here
}
constructor() {}
}
Now the directive has the value to display the content, we also need TemplateRef and ViewContainerRef instances. We can do that by injecting them by importing from @angular/core
:
import { TemplateRef, ViewContainerRef } from '@angular/core';
constructor(
private templateRef: TemplateRef<unknown>,
private vcr: ViewContainerRef
) {}
Now, we can make a use of ViewContainerRef's
reference this.vcr.createEmbeddedView(this.templateRef)
method to display and this.vcr.clear()
method to remove the content.
This is how the final code looks like
@Directive({
selector: '[customIf]',
})
export class IfDirective {
@Input() set customIf(value: boolean) {
this._renderTemplate(value)l
}
constructor(
private templateRef: TemplateRef<unknown>,
private vcr: ViewContainerRef
) {}
private _renderTemplate(show: boolean) {
this.vcr.clear();
if (show) {
this.vcr.createEmbeddedView(this.templateRef);
}
}
}
Wohoo! 🥳 we have successfully create *customIf
now let's focus on creating else template
So, let us understand how it works?
How do we achieve this behaviour in our custom directive? This is where understanding the "syntactic sugar" that stands behind the structural directives is crucial.
If we observe the above image example, it means that the else property is actually becoming ngIfElse input parameter.
So, we can access the else template by selector (customIf) + else (Else)
= customIfElse
@Input() customIfElse?: TemplateRef<unknown>;
Now, the code looks like
@Directive({
selector: '[customIf]',
})
export class IfDirective {
@Input() set customIf(value: boolean) {
this._renderTemplate(value)l
}
@Input() customIfElse?: TemplateRef<unknown>;
constructor(
private templateRef: TemplateRef<unknown>,
private vcr: ViewContainerRef
) {}
private _renderTemplate(show: boolean) {
this.vcr.clear();
if (show) {
this.vcr.createEmbeddedView(this.templateRef);
} else if (this.customIfElse) {
this.vcr.createEmbeddedView(this.customIfElse);
}
}
}
Summary
In this article, we've learnt how to create a simple custom structural directive that handles additional inputs. We've covered the syntactic sugar that stands behind the structural directive, and how it translates into directive's inputs.
In case you have any questions, you can always tweet or DM me at @DeekshithrajB. I'm always happy to help!
Connect with me over linkedIn: Deekshith Raj Basa
Top comments (1)
Hi thank you for the post. Can you extend the code demoes with stackblitz.com and for example how's customIf handles streams (rxjs) seems it wont work with not-resolved arguments.