Directives in Angular
Directives are an important part of any Angular application. We use Built-in directives such as the RouterOutlet, *ngFor, or ngModel or custom directive which are made by the developers or can be imported to the
There are three main types of directives which are:
- Attribute Directives: These are custom attributes that are integrated into existing DOM elements and add new features to these elements.
- Structural directives: These are also attributes that are integrated into existing DOM elements. These directives manipulate the inner content inside the DOM.
- Components: This is actually an Angular component within another Angular component. So each component has a component class and often has an HTML and a CSS file attached to it.
Attribute Directives:
In this example, we will build a directive that will be attached to an HTML input element and will allow inserting only numbers to it.
Here is how you combine an attribute directive in an HTML code:
<input type="text" id="txtIDNum" placeholder="ID number" maxlength="9" appInputNumbersOnly>
appInputNumbersOnly is the attribute directive itself. We will now learn how to build this directive.
Let's start by creating a directive file. Open the command line and type the following command:
ng g directive input-numbers-only
This command creates two files:
- input-numbers-only.directive.ts: this is the actual directive code file.
- input-numbers-only.directive.spec.ts: this is a file for executing unit tests for the newly created directive.
Let's look now at our module file. We can see that we have imported the directive into the module:
import { InputNumbersOnlyDirective } from './input-numbers-only.directive';
We have to declare the new directive:
@NgModule({
declarations: [
...
InputNumbersOnlyDirective,
...
],
...
})
Now let's take a look at the newly created directive file:
import { Directive } from '@angular/core';
@Directive({
selector: '[appInputNumbersOnly]'
})
export class InputNumbersOnlyDirective {
constructor() { }
}
This is basically a typescript class. We see the there is a @Directive decorator. This decorator defines the selector property of the directive. The selector property is the name of the directive attribute to be combined in the DOM element.
Now we have to build a function that checks the key down event input and attach it to the event. There is a special decorator called @HostListener which attaches the function to the key down event. First, we have to import the decorator. this is done by the following command:
import { Directive, HostListener } from '@angular/core';
Now we can define the keyDown function inside the class:
@HostListener('keydown', ['$event'])
confirmFirst(event: KeyboardEvent) {
}
The "event" parameter is a keyboard event that contains various properties such as the key that was pressed and so forth. The function will return true if the keyboard pressed in a numerical keyboard and false if something else was pressed:
@HostListener('keydown', ['$event'])
confirmFirst(event: KeyboardEvent) {
if (!isNaN(Number(event.key))) {
return true;
} else {
return false;
}
}
Now, when we execute the app, we can see that we have the focus on an input field and try to press a non-numeric key nothing happens. we can insert only a numerical value.
Structural Directive:
Like the attribute directive, this is also an attribute that is inserted into an HTML element. In this tutorial, we will display the inner content of a DIV element if certain conditions will be fulfilled. Here is the implementation of the structural directive:
<div class="dot" *appIsAdmin="11111111">Hello Administrator</div>
In the example above we display the "Hello Administrator" message if the parameter of the appIsAdmin attribute is 11111111.
We will now create a directive in the same way we created the attribute directive in the last paragraph:
ng g directive is-admin
Here is the basic directive class template:
import { Directive } from '@angular/core';
@Directive({
selector: '[appIsAdmin]'
})
export class IsAdminDirective {
constructor() { }
}
Now we have to import the attribute value to the class (In our specific example the value is: "11111111"). There is a decorator for it: @Input
@Input() set appIsAdmin(isAdmin: string) {}
Where appIsAdmin is the attribute name and isAdmin is the variable that the attribute value is stored into it.
In order to manipulate the inner content of the directive's parent element, we have to import two objects via the constructor function:
constructor(private templateRef: TemplateRef<any>, private viewContainerRef: ViewContainerRef) { }
All that left is the code that checks the parameter of the directive is true or not. We check all this inside the appIsAdmin function that we built before:
@Input() set appIsAdmin(isAdmin: string) {
if (isAdmin == "11111111"){
this.viewContainerRef.createEmbeddedView(this.templateRef);
} else {
this.viewContainerRef.clear();
}
}
Components:
These are ordinary Angular components that are inserted inside other Angular components. Like any other component these components may have HTML, and CSS templates. The inner component is integrated inside the outer component like this:
<app-login-component></app-login-component>
An inner component may look like the following sample below:
The user enters his credentials and if these credentials are correct then we get the following screen:
Here is the full code of the new component:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-login-component',
templateUrl: './login-component.component.html',
styleUrls: ['./login-component.component.css']
})
export class LoginComponentComponent implements OnInit {
txtIDNum: string;
constructor() { }
ngOnInit() {
}
butSubmit(event)
{
console.log(event);
document.getElementById("stage1").style.display = "none";
document.getElementById("stage2").style.display = "block";
}
}
Here is the HTML code of the templateUrl attached to it:
<div class="Framework">
<div class="stage1" id="stage1">
<input type="text" id="txtIDNum" placeholder="מספר ת.ז" maxlength="9" appInputNumbersOnly [(ngModel)]="txtIDNum">
<input type="password" id="txtPassword" placeholder="סיסמה" maxlength="15">
<input type="button" id="butLogin" value="שלח" class="btn btn-primary" (click)="butSubmit($event)">
</div>
<div class="stage2" id="stage2">
<div class="subframework">
<div class="dot" *appIsAdmin="txtIDNum">שלום מנהל מערכת</div>
</div>
<div class="subframework">מספר זהות: {{txtIDNum}}</div>
</div>
</div>
and the stylesURL attached to it:
.Framework
{
border: 1px solid #cecece;
width: 200px;
border-radius: 10px;
margin-right: auto;
margin-left: auto;
padding: 10px 10px 10px 10px;
direction: rtl;
height: 128px;
}
input
{
margin-bottom: 0.5em;
}
input:last-child
{
margin-bottom: 0;
}
input[type="text"], input[type="password"]
{
padding-right: 0.2em;
}
.stage2
{
display: none;
}
.subframework
{
width: 100%;
display: table;
margin-bottom: 5px;
}
Conclusion:
The purpose of this article is to demonstrate the three forms of a directive in Angular application. A full sample code can be found at https://github.com/krasnoff/directive-tutorial-app.
I will be happy to receive any comments. Please send them to krasnoff.kobi@gmail.com