🎨 Angular Custom Directives: Teaching Your HTML New Tricks!
🏠The Magic Paintbrush Story
Imagine you have a magic paintbrush. When you wave it over any object in your room, that object gets superpowers!
- Wave it over your toy car → it can now glow in the dark!
- Wave it over your book → it changes color when you touch it!
- Wave it over your lamp → it turns on when you clap!
Custom Directives in Angular are exactly like this magic paintbrush!
They give ordinary HTML elements extraordinary abilities.
🎯 What Are Custom Directives?
A directive is a special instruction you attach to an HTML element.
Angular has built-in directives like *ngIf and *ngFor. But what if you want your OWN special behavior?
That’s where Custom Directives come in!
graph TD A[Your HTML Element] --> B[Add Custom Directive] B --> C[Element Gets Superpowers!] C --> D[Changes Color on Click] C --> E[Grows on Hover] C --> F[Anything You Dream Up!]
🛠️ Creating Your First Custom Directive
Let’s build a directive that makes any element turn yellow when clicked.
Step 1: Generate the Directive
ng generate directive highlight
This creates a file called highlight.directive.ts.
Step 2: Write the Directive
import { Directive } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor() {
console.log('Directive is alive!');
}
}
Step 3: Use It!
<p appHighlight>
Click me! I have superpowers!
</p>
That’s it! The [appHighlight] in brackets means it’s an attribute directive.
đź‘‚ HostListener: Hearing What Happens
Your directive needs ears to hear events like clicks, mouse movements, and keyboard presses.
HostListener is like giving your directive super-hearing!
The Simple Idea
When something happens → Do something
Real Example
import {
Directive,
HostListener
} from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
@HostListener('click')
onClick() {
alert('You clicked me!');
}
@HostListener('mouseenter')
onMouseEnter() {
console.log('Mouse came in!');
}
@HostListener('mouseleave')
onMouseLeave() {
console.log('Mouse went out!');
}
}
What Can HostListener Hear?
| Event | When It Fires |
|---|---|
click |
User clicks element |
mouseenter |
Mouse goes over element |
mouseleave |
Mouse leaves element |
keydown |
User presses a key |
focus |
Element gets focus |
Getting Event Details
Want to know WHICH key was pressed?
@HostListener('keydown', ['$event'])
onKeyDown(event: KeyboardEvent) {
console.log('Key pressed:', event.key);
}
The ['$event'] passes the full event object to your function!
🎨 HostBinding: Changing How It Looks
HostListener hears events. HostBinding changes appearance!
Think of HostBinding as your directive’s paintbrush.
The Simple Idea
Connect a property → Element style changes
Real Example
import {
Directive,
HostBinding,
HostListener
} from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
@HostBinding('style.backgroundColor')
bgColor = 'transparent';
@HostListener('click')
onClick() {
this.bgColor = 'yellow';
}
}
Magic! When you click, the background turns yellow!
What Can HostBinding Change?
// Change background color
@HostBinding('style.backgroundColor')
bgColor = 'red';
// Change text color
@HostBinding('style.color')
textColor = 'white';
// Add a CSS class
@HostBinding('class.active')
isActive = true;
// Change any attribute
@HostBinding('attr.title')
title = 'Hello!';
Complete Example: Color Toggle
@Directive({
selector: '[appColorToggle]'
})
export class ColorToggleDirective {
private isHighlighted = false;
@HostBinding('style.backgroundColor')
bgColor = 'white';
@HostBinding('style.color')
textColor = 'black';
@HostListener('click')
toggle() {
this.isHighlighted = !this.isHighlighted;
if (this.isHighlighted) {
this.bgColor = 'purple';
this.textColor = 'white';
} else {
this.bgColor = 'white';
this.textColor = 'black';
}
}
}
đź”§ ElementRef: Touching the Real Element
Sometimes you need to directly touch the HTML element itself.
ElementRef gives you a direct reference to the actual DOM element.
The Simple Idea
ElementRef = The actual HTML element
Basic Example
import {
Directive,
ElementRef
} from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef) {
// Access the real HTML element!
console.log(el.nativeElement);
}
}
Changing the Element Directly
@Directive({
selector: '[appBigText]'
})
export class BigTextDirective {
constructor(private el: ElementRef) {
this.el.nativeElement.style.fontSize = '24px';
this.el.nativeElement.style.fontWeight = 'bold';
}
}
⚠️ Warning About ElementRef
Directly touching nativeElement works, but it’s not the safest way.
Why? Because:
- It might not work on all platforms (like server-side rendering)
- It skips Angular’s safety checks
Better approach: Use Renderer2 (coming next!)
🛡️ Renderer2: The Safe Way to Change Elements
Renderer2 is Angular’s official tool for safely changing elements.
Think of it as wearing safety gloves when painting!
Why Use Renderer2?
| ElementRef (Direct) | Renderer2 (Safe) |
|---|---|
| Works in browser only | Works everywhere |
| No security checks | XSS protection |
| Faster but riskier | Safer and proper |
Basic Example
import {
Directive,
ElementRef,
Renderer2
} from '@angular/core';
@Directive({
selector: '[appSafeHighlight]'
})
export class SafeHighlightDirective {
constructor(
private el: ElementRef,
private renderer: Renderer2
) {
// Safe way to set style!
this.renderer.setStyle(
this.el.nativeElement,
'backgroundColor',
'yellow'
);
}
}
Renderer2 Superpowers
// Set a style
this.renderer.setStyle(
element, 'color', 'blue'
);
// Remove a style
this.renderer.removeStyle(
element, 'color'
);
// Add a CSS class
this.renderer.addClass(
element, 'highlight'
);
// Remove a CSS class
this.renderer.removeClass(
element, 'highlight'
);
// Set an attribute
this.renderer.setAttribute(
element, 'title', 'Hello!'
);
// Remove an attribute
this.renderer.removeAttribute(
element, 'title'
);
🎪 Putting It All Together
Here’s a complete directive that uses EVERYTHING we learned!
import {
Directive,
ElementRef,
Renderer2,
HostListener,
HostBinding,
Input
} from '@angular/core';
@Directive({
selector: '[appMagicBox]'
})
export class MagicBoxDirective {
@Input() highlightColor = 'yellow';
@HostBinding('style.transition')
transition = 'all 0.3s ease';
@HostBinding('style.cursor')
cursor = 'pointer';
constructor(
private el: ElementRef,
private renderer: Renderer2
) {}
@HostListener('mouseenter')
onMouseEnter() {
this.renderer.setStyle(
this.el.nativeElement,
'backgroundColor',
this.highlightColor
);
this.renderer.setStyle(
this.el.nativeElement,
'transform',
'scale(1.05)'
);
}
@HostListener('mouseleave')
onMouseLeave() {
this.renderer.removeStyle(
this.el.nativeElement,
'backgroundColor'
);
this.renderer.setStyle(
this.el.nativeElement,
'transform',
'scale(1)'
);
}
}
Using It in HTML
<div appMagicBox highlightColor="coral">
Hover over me for magic!
</div>
<button appMagicBox highlightColor="#00ff00">
I glow green!
</button>
🗺️ The Big Picture
graph TD A[Custom Directive] --> B[HostListener] A --> C[HostBinding] A --> D[ElementRef] A --> E[Renderer2] B --> F[Listens to Events] C --> G[Binds to Properties] D --> H[Access DOM Element] E --> I[Safely Modify DOM] F --> J[click, hover, keypress...] G --> K[style, class, attr...] H --> L[nativeElement] I --> M[setStyle, addClass...]
🎓 Quick Reference
| Tool | Purpose | Use When |
|---|---|---|
| @Directive | Create directive | Always - it’s the foundation |
| HostListener | Listen to events | Reacting to user actions |
| HostBinding | Bind properties | Changing styles/classes |
| ElementRef | Access element | Need the actual element |
| Renderer2 | Modify safely | Best practice for DOM changes |
đź’ˇ Remember This!
- Directives = Superpowers for HTML elements
- HostListener = Your directive’s ears (hearing events)
- HostBinding = Your directive’s paintbrush (changing looks)
- ElementRef = The actual element (use carefully)
- Renderer2 = The safe way (always prefer this)
You now have the power to teach ANY HTML element new tricks!
Go build something amazing! 🚀