Angular Directives
Learn Angular directives, including components, attribute directives, structural directives, modern control flow, custom directives, Renderer2, HostListener, enterprise use cases, performance, and best practices.
Learning Objectives
By the end of this lesson, you will understand:
- What directives are in Angular.
- Why Angular needs directives.
- How directives extend the behavior of HTML.
- The major types of Angular directives.
- How built-in directives work.
- How modern Angular control flow relates to traditional structural directives.
- How to create a custom attribute directive.
- How directives fit into enterprise application architecture.
- Real-world use cases for directives.
- Performance considerations and best practices.
- Common directive mistakes.
- Frequently asked Angular interview questions.
Introduction
Imagine you're building an e-commerce application.
On the product page, you need to:
- Highlight products that are on sale.
- Disable unavailable products.
- Change the appearance of premium products.
- Show an "Out of Stock" message conditionally.
- Display hundreds of products dynamically.
- Apply special behavior when users hover over an element.
- Show administrative controls only to authorized users.
You could write JavaScript code to manually find each DOM element and modify it.
But as your application grows, that approach quickly becomes difficult to maintain.
Angular provides a much cleaner solution:
Directives.
Directives allow developers to attach reusable behavior to elements and components in Angular templates.
In simple terms:
A directive tells Angular how an element should behave, appear, or participate in the application.
Understanding directives is important because they reveal one of Angular's core architectural ideas: declarative UI development.
Instead of manually telling the browser how to manipulate the DOM step by step, you declare the desired behavior, and Angular manages the implementation.
A Real-World Story
Imagine a large banking application used by millions of customers.
Different users have different roles:
- Customer
- Relationship Manager
- Branch Manager
- Auditor
- Administrator
The application contains hundreds of screens.
Administrators can see:
Approve TransactionDelete UserManage Permissions
Regular customers should never see these controls.
One option would be to repeat permission-checking logic across hundreds of components.
That would create:
- Duplicate code
- Inconsistent behavior
- Maintenance problems
- Increased risk of mistakes
Instead, the development team could create reusable UI behavior such as:
<button *appHasRole="'ADMIN'">Manage Users</button>
The custom directive centralizes the UI visibility behavior.
If the authorization-display rule changes, developers update the directive rather than hundreds of templates.
Important: Hiding a button in the UI is not security by itself. The backend must still enforce authorization. The directive only controls presentation.
This demonstrates the real power of directives: reusable behavior applied declaratively across an application.
What Is an Angular Directive?
A directive is a class that adds behavior to elements in an Angular application.
Directives can:
- Change an element's appearance.
- Respond to user interactions.
- Add reusable UI behavior.
- Work with element properties and styles.
- Historically add or remove template fragments through structural directives.
Consider this simple example:
<p appHighlight>Important Information</p>
Here:
appHighlight
could be a custom directive that changes the background when the user moves the mouse over the paragraph.
The HTML element remains a paragraph.
The directive simply adds new behavior to it.
Why Do We Need Directives?
Consider an enterprise application containing hundreds of screens.
Several screens may need the same behavior:
- Highlight invalid fields.
- Show tooltips.
- Detect clicks outside a popup.
- Automatically focus an input.
- Restrict UI elements by role.
- Apply standardized accessibility behavior.
- Track specific user interactions.
Without directives, developers may duplicate the same DOM-related logic across multiple components.
With directives, reusable behavior can be centralized.
Conceptually:
Without DirectiveComponent A -> Duplicate LogicComponent B -> Duplicate LogicComponent C -> Duplicate LogicComponent D -> Duplicate Logic
With a directive:
Shared Directive/ | \/ | \▼ ▼ ▼Component A Component B Component C
The same behavior can now be reused throughout the application.
Directive Architecture
At a high level, a directive sits between Angular's template system and the element to which it is attached.
Directive Architecture
Template Behavior Attached to Elements
For example, a custom highlight directive might:
- Be attached to an element.
- Listen for
mouseenter. - Apply a background style.
- Listen for
mouseleave. - Restore the original style.
The component using the directive does not need to implement this behavior itself.
Types of Directives in Angular
Angular directives are commonly discussed in three categories:
- Components
- Attribute Directives
- Structural Directives
Let's understand each one.
1. Components
A component is technically a specialized type of directive with its own template.
For example:
@Component({selector: 'app-product-card',templateUrl: './product-card.component.html'})export class ProductCardComponent {}
Used as:
<app-product-card></app-product-card>
Components create reusable UI blocks.
A component normally includes:
- TypeScript logic
- HTML template
- Styles
- Component metadata
You can think of it like this:
Directive│├── Attribute Directive│├── Structural Directive│└── Component│└── Has Its Own View
Components are therefore a central part of Angular's directive model.
2. Attribute Directives
Attribute directives modify the behavior or appearance of an existing element.
They do not normally create an entirely new UI structure.
Examples of commonly encountered built-in directives include:
ngClassngStyle
Custom directives are also frequently created as attribute directives.
Example:
<p appHighlight>Special Offer</p>
The paragraph still exists.
The directive only changes how it behaves or appears.
Example: ngClass
ngClass allows CSS classes to be applied dynamically.
Component:
export class ProductComponent {isAvailable = true;}
Template:
<div [ngClass]="{'available': isAvailable,'unavailable': !isAvailable}">Product Status</div>
If:
isAvailable = true;
Angular applies:
available
If the value changes to false, Angular applies:
unavailable
This is useful when styling depends on application state.
Real-World Example: Transaction Status
Imagine a banking dashboard.
Transactions may have the following statuses:
SUCCESSPENDINGFAILED
You may want each status to have a different visual appearance.
Conceptually:
<span[ngClass]="{'success': transaction.status === 'SUCCESS','pending': transaction.status === 'PENDING','failed': transaction.status === 'FAILED'}">{{ transaction.status }}</span>
The UI now reflects the transaction state automatically.
This is a practical example of combining Angular's state-driven rendering with directive-based styling.
Example: ngStyle
ngStyle can apply inline styles dynamically.
Component:
export class ProductComponent {stock = 5;}
Template:
<p[ngStyle]="{'font-weight': 'bold','opacity': stock === 0 ? 0.5 : 1}">Available Stock: {{ stock }}</p>
The element's appearance changes based on application state.
For simple style bindings, Angular also supports direct style binding:
<p [style.opacity]="stock === 0 ? 0.5 : 1">Available Stock: {{ stock }}</p>
Use the simplest approach that keeps the template readable.
3. Structural Directives
Structural directives historically control whether template fragments are created, removed, or repeated.
Common traditional examples include:
*ngIf*ngFor*ngSwitchCase
For example:
<div *ngIf="isLoggedIn">Welcome back!</div>
Angular conditionally creates this part of the view.
Another example:
<li *ngFor="let product of products">{{ product.name }}</li>
Angular creates one list item for every product.
Modern Angular Control Flow
Modern Angular also provides built-in template control-flow syntax such as:
@if@for@switch
For example:
@if (isLoggedIn) {<p>Welcome back!</p>} @else {<p>Please log in.</p>}
For lists:
@for (product of products; track product.id) {<p>{{ product.name }}</p>}
This modern syntax is important because new Angular applications may use built-in control flow instead of traditional *ngIf and *ngFor.
Therefore, as an Angular developer, you should understand both:
Traditional Structural Directives│├── *ngIf├── *ngFor└── *ngSwitchModern Built-in Control Flow│├── @if├── @for└── @switch
You'll explore conditional rendering and lists in dedicated lessons.
Attribute vs Structural Directive
Understanding this difference is a common interview topic.
Attribute Directive
Changes an existing element's behavior or appearance.
Element Exists│▼Directive Applied│▼Behavior / Appearance Changes
Example:
<p appHighlight>Important</p>
Structural Directive
Traditionally changes the structure of the rendered view.
Condition / Collection│▼Structural Directive│▼Template Instantiated│▼DOM Structure Changes
Example:
<div *ngIf="isVisible">Content</div>
The important distinction is:
Attribute directives modify behavior or appearance, while structural directives influence the rendered template structure.
Creating a Custom Directive
Let's create a reusable highlight directive.
Run:
ng generate directive highlight
Shortcut:
ng g d highlight
A simplified directive may look like:
import { Directive, ElementRef } from '@angular/core';@Directive({selector: '[appHighlight]'})export class HighlightDirective {constructor(private element: ElementRef) {this.element.nativeElement.style.backgroundColor = 'yellow';}}
Use it in a template:
<p appHighlight>This content is highlighted.</p>
Angular detects:
appHighlight
and applies the directive behavior.
A Better Approach with Renderer2
Direct DOM manipulation should generally be avoided when a framework abstraction can be used.
Instead of directly changing:
element.nativeElement.style
Angular applications can use Renderer2.
Example:
import {Directive,ElementRef,Renderer2} from '@angular/core';@Directive({selector: '[appHighlight]'})export class HighlightDirective {constructor(private element: ElementRef,private renderer: Renderer2) {this.renderer.setStyle(this.element.nativeElement,'backgroundColor','yellow');}}
This provides a cleaner abstraction for DOM operations.
Adding User Interaction with HostListener
Suppose you want highlighting only when the user hovers over an element.
import {Directive,ElementRef,HostListener,Renderer2} from '@angular/core';@Directive({selector: '[appHighlight]'})export class HighlightDirective {constructor(private element: ElementRef,private renderer: Renderer2) {}@HostListener('mouseenter')onMouseEnter(): void {this.renderer.setStyle(this.element.nativeElement,'backgroundColor','yellow');}@HostListener('mouseleave')onMouseLeave(): void {this.renderer.removeStyle(this.element.nativeElement,'backgroundColor');}}
Now the behavior becomes:
User Moves Mouse Over Element│▼mouseenter│▼HostListener│▼Highlight Directive│▼Renderer Updates Style
When the mouse leaves, the style is removed.
This logic can now be reused across multiple components.
Making a Directive Configurable
Reusable directives should often accept configuration.
For example:
<p [appHighlight]="'lightblue'">Important Information</p>
Conceptually, the directive could accept an input:
@Input() appHighlight = 'yellow';
Now different components can reuse the same directive with different configurations.
Highlight Directive│┌────────────┼────────────┐▼ ▼ ▼Yellow Blue Green
This is far more reusable than hardcoding one behavior.
Real-Time Example: Role-Based UI
Imagine an enterprise administration portal.
Different roles have different UI capabilities.
User│▼Authentication Service│▼User Roles│▼Role-Aware UI Directive│├── Admin -> Display Admin Control│└── Customer -> Do Not Display Admin Control
A template might conceptually use:
<button *appHasRole="'ADMIN'">Delete Account</button>
This keeps repetitive UI authorization-display logic centralized.
However, remember:
Frontend Visibility!=Backend Authorization
The server must always independently validate whether the user is authorized to perform the action.
This distinction is critical in enterprise security.
Real-World Enterprise Architecture
Consider a large Angular application:
Enterprise Architecture
Shared Directives Across Features
Shared directives can provide consistent behavior across the entire application.
For example:
shared/│├── directives/│ ├── autofocus.directive.ts│ ├── click-outside.directive.ts│ ├── tooltip.directive.ts│ └── permission.directive.ts
This architecture improves reuse and consistency.
Production Case Study: Enterprise Form Validation
Imagine an insurance company with hundreds of forms.
Each form contains fields such as:
- Customer Name
- Policy Number
- Address
- Phone Number
When validation fails, the company wants every invalid field to behave consistently.
Requirements:
- Highlight invalid fields.
- Move focus to the first invalid field.
- Display accessibility information.
- Track validation interactions.
Instead of implementing this behavior separately in every form component, the team can extract suitable reusable behavior into directives or shared form components.
The result:
Hundreds of Forms│▼Shared Validation Behavior│▼Consistent User Experience│▼Reduced Duplicate Code
This is where directives provide significant architectural value.
When Should You Create a Custom Directive?
Create a custom directive when:
- The same DOM-related behavior appears in multiple components.
- You need reusable interaction behavior.
- You need standardized UI behavior.
- The behavior naturally belongs to an existing element.
- Creating a full component would be unnecessary.
Examples:
- Autofocus
- Click Outside
- Tooltip
- Keyboard Shortcuts
- Drag Behavior
- Input Formatting
- Permission-Based UI Behavior
When Should You Avoid a Directive?
Don't create a directive simply because you can.
Avoid directives when:
- The behavior belongs entirely inside one component.
- The feature requires a complex independent UI.
- A reusable component would provide clearer encapsulation.
- Native Angular template features already solve the problem cleanly.
- The abstraction makes the application harder to understand.
Experienced Angular developers don't ask:
"Can I create a directive?"
They ask:
"Is a directive the clearest abstraction for this reusable behavior?"
Performance Considerations
Directives are powerful, but poor implementations can affect performance.
Be careful with directives that:
- Attach expensive event listeners.
- Perform heavy calculations frequently.
- Directly manipulate large sections of the DOM.
- Trigger unnecessary application updates.
- Are attached to thousands of elements.
For example, imagine attaching a complex mouse-move directive to 10,000 table cells.
10,000 Elements│▼10,000 Directive Instances│▼Large Number of Event Handlers│▼Potential Performance Cost
Always evaluate the scale at which a directive will operate.
Best Practices
- Keep directives focused on one responsibility.
- Use clear selector names.
- Prefer reusable and configurable directives.
- Avoid placing complex business logic inside directives.
- Use Angular abstractions such as
Renderer2when appropriate. - Clean up manually created resources and subscriptions when necessary.
- Keep authorization enforcement on the backend.
- Prefer modern Angular built-in control flow for new templates when appropriate.
- Don't create a directive when a simple binding is sufficient.
- Test shared directives independently.
Common Pitfalls
Overusing Directives
Not every behavior requires a custom directive.
Simple logic may be clearer with normal Angular bindings.
Direct DOM Manipulation
Avoid unnecessarily manipulating the DOM directly.
Prefer Angular-supported abstractions and declarative templates.
Putting Business Logic in Directives
A directive should usually provide reusable UI behavior.
Complex business rules belong in appropriate services or domain layers.
Confusing UI Permissions with Security
Hiding an element using a directive does not prevent API access.
The backend must always enforce authorization.
Creating Too Many Generic Directives
Over-abstraction can make code difficult to understand.
A directive should solve a clear, recurring problem.
Common Misconceptions
Misconception
Directives and Components are completely unrelated.
Reality
A Component is a specialized type of directive that includes its own template.
Misconception
Directives are only used for *ngIf and *ngFor.
Reality
Angular supports attribute directives, structural directives, and custom reusable directives for many UI behaviors.
Misconception
Modern Angular removed directives.
Reality
Directives remain a core Angular concept. Modern built-in control flow such as @if and @for provides newer syntax for common template-control scenarios, while custom directives remain valuable for reusable element behavior.
Advanced interview questions
Interview Prep
Practice concise answers, then expand each card for the explanation.
1BeginnerQuestionWhat is a directive in Angular?+
Answer
2BeginnerQuestionWhat are the main types of directives?+
Answer
3IntermediateQuestionWhat is the difference between an Attribute Directive and a Structural Directive?+
Answer
4IntermediateQuestionWhat is the difference between *ngIf and @if?+
Answer
*ngIf is the traditional structural directive approach for conditional rendering. Modern Angular provides built-in control-flow syntax using @if, which offers a more direct template syntax for conditional rendering. Developers maintaining existing applications should understand both approaches.5IntermediateQuestionWhy would you create a custom directive?+
Answer
6AdvancedQuestionShould role-based UI directives be considered a security mechanism?+
Answer
7AdvancedQuestionWhen would you choose a component instead of a directive?+
Answer
Summary
Angular Directives allow developers to extend HTML with reusable, application-specific behavior. They help transform static markup into dynamic interfaces where appearance, interactions, and rendered content can respond to application state.
The most important architectural idea is understanding the responsibility of each abstraction:
Component│└── Creates a reusable UI with its own viewAttribute Directive│└── Adds behavior to an existing elementStructural Directive│└── Traditionally controls template structureModern Control Flow│└── @if, @for, @switch for common rendering logic
In enterprise applications, custom directives can centralize recurring UI behaviors such as autofocus, keyboard interactions, tooltips, and permission-aware presentation. However, good Angular architecture means knowing when not to create a directive and choosing the simplest abstraction that clearly solves the problem.
In the next lesson, Angular Events, you'll learn how Angular applications respond to user interactions such as clicks, typing, keyboard events, form submissions, and other browser events-and how those events flow from the user interface into your application's business logic.