Angular Directives: A Detailed Guide
Welcome to this comprehensive blog post about Angular Directives. In this blog, we will deep-dive into the concepts of Angular Directives, their types, and their usage with practical examples.
Introduction to Angular Directives
Angular directives are classes that add additional behavior to elements in your Angular applications. They are one of the core features that power Angular applications. Angular directives allow you to create highly dynamic and responsive web applications by enabling you to manipulate the DOM (Document Object Model).
Types of Directives in Angular
There are three types of directives in Angular:
Component Directives : These are the most common directives and are used to create custom HTML elements. Angular components are essentially classes that encapsulate the code required for a specific functionality.
Attribute Directives : These directives change the appearance, behavior, or layout of DOM elements. They're used as attributes in HTML elements.
Structural Directives : These directives alter the layout by adding, removing, and manipulating DOM elements. They usually start with an asterisk (*) symbol.
Component Directives
In Angular, every application is a tree of components. Component directives form the main building blocks of Angular applications. They encapsulate the template HTML, data, and behavior of a view. An example of a component directive is as follows:
import { Component } from '@angular/core';
@Component({
selector: 'app-my-component',
template: `<h1>Hello, world!</h1>`
})
export class MyComponent {
// Component logic goes here
}
In this code, the @Component
decorator indicates that the class MyComponent
is a component. It provides metadata about the component, including its selector (the name you give to the component when it is represented as an HTML element) and its template.
Attribute Directives
Attribute directives are used to change the behavior or appearance of an element, component, or another directive. Here is an example of an attribute directive:
import { Directive, ElementRef, Renderer2 } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef, private renderer: Renderer2) {
renderer.setStyle(el.nativeElement, 'backgroundColor', 'yellow');
}
}
In this code, the @Directive
decorator tells Angular that the HighlightDirective
class is a directive. It applies a yellow background color to any element that's annotated with appHighlight
.
Structural Directives
Structural directives are responsible for the HTML layout. They shape or reshape the DOM's structure, typically by adding, removing, and manipulating the elements to which they are attached. Examples include *ngFor
and *ngIf
.
Here's an example usage of the *ngIf
structural directive:
<p *ngIf="condition">Content to show if condition is true</p>
In this code, the paragraph element will only be included in the DOM if the condition is true.
Directive Selectors
In Angular, a directive's selector is the name used to identify the directive within HTML tags. The selector is defined in the directive configuration object. Different types of selectors can be used:
element-name
: Select by element name..class
: Select by class name.[attribute]
: Select by attribute name.[attribute=value]
: Select by attribute name and value.
@Directive({ selector: '[appHighlight]' })
In this example, appHighlight
is the selector. We can use this directive in a template as follows:
<p appHighlight>Highlighted text</p>
Built-In Directives
Angular comes with several built-in directives. Here are some of them:
ngClass
: This directive dynamically binds one or more CSS classes to an HTML element. The value can be a string, array, or object.ngStyle
: This directive dynamically binds one or more inline styles to an HTML element.ngModel
: This directive creates a two-way data binding on a form input element.
Communicating with Directives
Sometimes it's necessary to pass data into our directives. For this, Angular provides @Input
and @Output
.
@Input
allows data to flow from a component into a directive, and @Output
allows a directive to emit events to a component.
import { Directive, Input } from '@angular/core';
@Directive({
selector: '[appCustomDirective]'
})
export class CustomDirective {
@Input('appCustomDirective') data: any;
// Further implementation
}
Lifecycle Hooks in Directives
Just like Angular components, directives also have lifecycle hooks. The lifecycle hooks give visibility into key life moments and allow you to execute code at specific times. The following are the lifecycle hooks available to directives:
ngOnChanges()
: Called beforengOnInit()
and whenever one or more data-bound input properties change.ngOnInit()
: Called once, after the firstngOnChanges()
.ngDoCheck()
: Called during every change detection run.ngAfterContentInit()
: Called once after the firstngDoCheck()
.ngAfterContentChecked()
: Called after thengAfterContentInit()
and every subsequentngDoCheck()
.ngAfterViewInit()
: Called once after the view (and child views) are initialized.ngAfterViewChecked()
: Called after thengAfterViewInit()
and every subsequentngAfterViewChecked()
.ngOnDestroy()
: Called just before Angular destroys the directive or component.
Directive's Scope
In Angular, a directive can either have an isolated scope or can inherit the scope from its parent.
Isolated Scope : Here, the directive has its own scope and does not inherit its parent scope. This is useful when we want the directive to be reusable and self-contained.
Inherited Scope : In this case, the directive inherits the scope from its parent. This is useful when we want to manipulate something that's defined in the parent scope.
This concept is more prevalent in AngularJS, and in Angular, this is handled more implicitly through property and event bindings.
Testing Directives
Like any other part of an application, directives need to be tested as well. Angular provides a number of testing utilities and practices for testing directives. TestBed is commonly used to create a dynamic test component that applies the directive.
import { Component } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CustomDirective } from './custom.directive';
@Component({
template: `<div appCustom></div>`
})
class TestComponent {}
describe('CustomDirective', () => {
let component: TestComponent;
let fixture: ComponentFixture<TestComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestComponent, CustomDirective]
});
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
});
it('should create an instance', () => {
expect(component).toBeDefined();
});
});
Conclusion
Directives are a vital part of Angular applications, and understanding them is key to mastering the framework. Component directives help you organize your application into self-contained, reusable pieces. Attribute directives allow you to modify the behavior and appearance of elements and components, and structural directives give you the power to alter the layout of the DOM.
Remember, while Angular provides a number of useful directives out-of-the-box, one of the most powerful aspects of directives is that you can create your own to encapsulate and reuse functionality throughout your application.
Thank you for reading this blog post. Stay tuned for more informative posts about Angular and its various features!