Angular Custom Directives
Learn Angular Custom Directives with detailed examples, lifecycle, ElementRef, Renderer2, HostListener, HostBinding, Dependency Injection, standalone directives, enterprise use cases, architecture diagrams, and interview questions.
Angular provides many built-in directives like ngIf, ngFor, ngClass, and ngStyle.
However, enterprise applications often require reusable UI behavior that isn't available out of the box. This is where Custom Directives become extremely useful.
Custom Directives allow developers to:
- Reuse UI behavior
- Encapsulate DOM manipulation
- Improve maintainability
- Reduce duplicate code
- Build reusable enterprise components
Custom Directives are a favorite interview topic for Senior Angular Developers, Tech Leads, and Angular Architects.
Table of Contents
- What are Custom Directives?
- Types of Directives
- Attribute Directives
- Structural Directives
- Creating Custom Directives
- HostListener
- HostBinding
- Renderer2
- ElementRef
- Dependency Injection
- Standalone Directives
- Enterprise Examples
- Performance Best Practices
- Top 10 Interview Questions
- Summary
What are Custom Directives?
A Custom Directive is a reusable class that extends the behavior of HTML elements.
Unlike Components, directives do not have their own templates.
They simply modify existing elements.
Examples:
- Highlight text
- Change colors
- Show tooltips
- Restrict input
- Auto focus
- Permission checks
- Lazy image loading
- Infinite scrolling
Angular Directive Architecture
flowchart TD
Angular
Angular --> Component
Angular --> AttributeDirective
Angular --> StructuralDirective
Component --> HTML
AttributeDirective --> HTML
StructuralDirective --> HTML
Directive Types
| Directive | Purpose |
|---|---|
| Component | UI + Template |
| Attribute Directive | Changes appearance/behavior |
| Structural Directive | Changes DOM layout |
Attribute Directive Example
flowchart LR
Component
Component --> HighlightDirective
HighlightDirective --> HTML
HTML --> Browser
Creating a Custom Directive
Generate using Angular CLI
ng generate directive highlight
or
ng g d highlight
Basic Highlight Directive
import {
Directive,
ElementRef
} from '@angular/core';
@Directive({
selector:'[appHighlight]',
standalone:true
})
export class HighlightDirective{
constructor(
private element:
ElementRef
){
this.element.nativeElement
.style.backgroundColor='yellow';
}
}
Usage
<p appHighlight>
Highlighted Text
</p>
Output
Yellow Background
Directive Execution Flow
sequenceDiagram
participant Template
participant Directive
participant DOM
participant Browser
Template->>Directive: appHighlight
Directive->>DOM: Modify Element
DOM-->>Browser: Render
Why Renderer2?
Direct DOM manipulation through ElementRef.nativeElement should be minimized because it can make code less portable and harder to test.
Angular recommends Renderer2 for DOM updates.
Using Renderer2
import {
Directive,
ElementRef,
Renderer2
} from '@angular/core';
@Directive({
selector:'[appHighlight]',
standalone:true
})
export class HighlightDirective{
constructor(
private element:ElementRef,
private renderer:Renderer2
){
this.renderer.setStyle(
this.element.nativeElement,
'background',
'yellow'
);
}
}
Advantages
- Platform independent
- Better security
- Easier testing
- Works with server-side rendering
Renderer2 Architecture
flowchart LR
Directive
Directive --> Renderer2
Renderer2 --> DOM
DOM --> Browser
Using HostListener
HostListener listens to events on the host element.
Example
import {
Directive,
ElementRef,
HostListener,
Renderer2
} from '@angular/core';
@Directive({
selector:'[appHover]',
standalone:true
})
export class HoverDirective{
constructor(
private element:ElementRef,
private renderer:Renderer2
){}
@HostListener('mouseenter')
mouseEnter(){
this.renderer.setStyle(
this.element.nativeElement,
'background',
'lightblue'
);
}
@HostListener('mouseleave')
mouseLeave(){
this.renderer.removeStyle(
this.element.nativeElement,
'background'
);
}
}
Usage
<div appHover>
Move Mouse Here
</div>
Event Flow
flowchart TD
MouseEnter
MouseEnter --> HostListener
HostListener --> Renderer2
Renderer2 --> DOM
Using HostBinding
HostBinding binds properties of the host element.
import {
Directive,
HostBinding,
HostListener
} from '@angular/core';
@Directive({
selector:'[appBorder]',
standalone:true
})
export class BorderDirective{
@HostBinding('style.border')
border='2px solid blue';
@HostBinding('style.borderRadius')
radius='8px';
@HostListener('mouseenter')
hover(){
this.border='2px solid red';
}
@HostListener('mouseleave')
leave(){
this.border='2px solid blue';
}
}
HostBinding Architecture
flowchart LR
HostBinding
HostBinding --> ElementStyle
ElementStyle --> Browser
Passing Values to Directives
Directives can receive inputs.
import {
Directive,
ElementRef,
Input,
Renderer2
} from '@angular/core';
@Directive({
selector:'[appColor]',
standalone:true
})
export class ColorDirective{
@Input()
appColor='blue';
constructor(
private element:ElementRef,
private renderer:Renderer2
){}
ngOnInit(){
this.renderer.setStyle(
this.element.nativeElement,
'color',
this.appColor
);
}
}
Usage
<p
[appColor]="'green'">
Angular
</p>
Structural Directive Example
import {
Directive,
Input,
TemplateRef,
ViewContainerRef
} from '@angular/core';
@Directive({
selector:'[appIfRole]',
standalone:true
})
export class IfRoleDirective{
constructor(
private template:
TemplateRef<unknown>,
private view:
ViewContainerRef
){}
@Input()
set appIfRole(hasAccess:boolean){
this.view.clear();
if(hasAccess){
this.view.createEmbeddedView(
this.template
);
}
}
}
Usage
<div *appIfRole="isAdmin">
Admin Menu
</div>
Structural Directive Flow
flowchart TD
Condition
Condition --> True
Condition --> False
True --> CreateView
False --> ClearView
Dependency Injection
Directives support Angular Dependency Injection.
Example
@Injectable({
providedIn:'root'
})
export class ThemeService{
currentColor(){
return 'green';
}
}
Directive
constructor(
private theme:
ThemeService,
private renderer:
Renderer2,
private element:
ElementRef
){}
Standalone Directives
Angular supports standalone directives.
@Directive({
selector:'[appFocus]',
standalone:true
})
export class FocusDirective{}
Standalone Component
@Component({
standalone:true,
imports:[FocusDirective]
})
export class HomeComponent{}
Auto Focus Directive
import {
AfterViewInit,
Directive,
ElementRef
} from '@angular/core';
@Directive({
selector:'[appAutoFocus]',
standalone:true
})
export class AutoFocusDirective
implements AfterViewInit{
constructor(
private element:ElementRef
){}
ngAfterViewInit(){
this.element.nativeElement.focus();
}
}
Usage
<input appAutoFocus>
Numbers Only Directive
import {
Directive,
HostListener
} from '@angular/core';
@Directive({
selector:'[appNumbersOnly]',
standalone:true
})
export class NumbersOnlyDirective{
@HostListener('keypress',['$event'])
allowNumbers(event:KeyboardEvent){
const key=event.key;
if(!/[0-9]/.test(key)){
event.preventDefault();
}
}
}
Usage
<input appNumbersOnly>
Enterprise Banking Example
Imagine an online banking application.
Custom Directives:
- Highlight overdue loans
- Hide admin menu
- Restrict account input
- Auto focus login field
- Highlight negative balance
- Copy account number
- Permission-based rendering
flowchart TD
Dashboard
Dashboard --> HighlightDirective
Dashboard --> PermissionDirective
Dashboard --> AutoFocusDirective
Dashboard --> TooltipDirective
HighlightDirective --> LoanCard
PermissionDirective --> AdminMenu
AutoFocusDirective --> Login
TooltipDirective --> Balance
Performance Best Practices
| Practice | Benefit |
|---|---|
| Prefer Renderer2 | Platform independence |
| Keep directives lightweight | Faster rendering |
| Avoid business logic | Better separation of concerns |
| Reuse directives | Less duplicate code |
| Use standalone directives | Simpler architecture |
| Minimize direct DOM access | Better compatibility |
Common Mistakes
Using ElementRef Excessively
❌
element.nativeElement.style.color='red';
Prefer
renderer.setStyle(...)
Business Logic Inside Directives
Avoid putting API calls or complex business rules inside directives.
Large Directives
Split directives into smaller reusable units.
Memory Leaks
If a directive subscribes to Observables or registers custom listeners, clean them up appropriately (for example, using DestroyRef or lifecycle cleanup).
Advantages
| Feature | Benefit |
|---|---|
| Reusable | Write once |
| Cleaner Components | Better architecture |
| Standalone | Easy imports |
| Renderer2 | Safe DOM manipulation |
| HostListener | Event handling |
| HostBinding | Dynamic properties |
| DI Support | Reusable services |
| Lightweight | Better performance |
Real-World Enterprise Use Cases
| Scenario | Directive |
|---|---|
| Banking | Permission Directive |
| HR | Employee Highlight |
| Insurance | Policy Status Color |
| E-Commerce | Product Badge |
| Healthcare | Critical Patient Highlight |
| CRM | VIP Customer Highlight |
| Logistics | Shipment Status |
| Education | Grade Highlight |
Top 10 Angular Custom Directives Interview Questions
1. What is a Custom Directive?
Answer
A Custom Directive is a reusable Angular class that adds behavior or modifies the appearance of existing DOM elements without having its own template.
2. What are the two major types of directives?
Answer
- Attribute Directives
- Structural Directives
3. What is the difference between a Component and a Directive?
| Component | Directive |
|---|---|
| Has a template | No template |
| Creates UI | Modifies existing UI |
Uses @Component |
Uses @Directive |
4. Why is Renderer2 preferred over ElementRef?
Answer
Renderer2 provides a platform-independent and testable way to modify the DOM while reducing direct dependency on browser-specific APIs.
5. What is HostListener?
Answer
@HostListener listens for events on the host element and invokes a method when those events occur.
Example:
@HostListener('click')
onClick() {}
6. What is HostBinding?
Answer
@HostBinding binds a property of the directive to a property or attribute of the host element.
Example:
@HostBinding('style.color')
color = 'red';
7. Can directives receive inputs?
Answer
Yes.
Use the @Input() decorator to configure directive behavior from templates.
8. Can services be injected into directives?
Answer
Yes.
Angular Dependency Injection works in directives just as it does in components and services.
9. Are standalone directives supported?
Answer
Yes.
Angular supports standalone directives that can be imported directly into standalone components.
10. What are the best practices for custom directives?
Answer
- Prefer Renderer2 over direct DOM manipulation.
- Keep directives focused on UI behavior.
- Avoid business logic.
- Create reusable directives.
- Use standalone directives in modern Angular.
- Clean up subscriptions and event listeners.
- Write unit tests for directive behavior.
Interview Cheat Sheet
| Topic | Remember |
|---|---|
| Decorator | @Directive |
| DOM Access | ElementRef |
| Preferred DOM API | Renderer2 |
| Events | @HostListener |
| Host Properties | @HostBinding |
| Inputs | @Input() |
| Structural APIs | TemplateRef, ViewContainerRef |
| Standalone | Supported |
| Dependency Injection | Supported |
| Best Practice | Keep directives reusable |
Summary
Custom Directives are a powerful Angular feature for encapsulating reusable UI behavior without creating new components. They allow developers to modify element appearance, respond to user interactions, and dynamically manipulate the DOM while promoting clean architecture and code reuse. By combining Renderer2, HostListener, HostBinding, Dependency Injection, and Standalone Directives, enterprise Angular applications can build maintainable, scalable, and reusable UI behavior with minimal duplication.
Key Takeaways
- ✔ Custom Directives extend HTML behavior.
- ✔ Attribute Directives modify existing elements.
- ✔ Structural Directives create or remove views.
- ✔ Prefer
Renderer2over direct DOM manipulation. - ✔ Use
HostListenerfor event handling. - ✔ Use
HostBindingfor host element properties. - ✔ Support configurable behavior with
@Input(). - ✔ Standalone Directives simplify modern Angular development.
- ✔ Keep directives lightweight and reusable.
- ✔ Custom Directives are a common Angular interview topic.
Next Article
➡️ 30-Template-Reference-Variables-QA.md