Angular Tutorial 0/42 lessons ~6 min read Lesson 10

    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.

    Course progress0%
    Focus
    32 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    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:

    text
    Approve Transaction
    Delete User
    Manage 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:

    html
    <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:

    html
    <p appHighlight>
    Important Information
    </p>

    Here:

    text
    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:

    text
    Without Directive
    Component A -> Duplicate Logic
    Component B -> Duplicate Logic
    Component C -> Duplicate Logic
    Component D -> Duplicate Logic

    With a directive:

    text
    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

    reusable behavior
    1
    Angular Template
    The developer declares behavior
    2
    Directive Applied
    Angular attaches directive logic
    3
    Read State
    Directive reads inputs or app state
    4
    Handle Events
    Directive reacts to DOM or host events
    5
    DOM Element
    Behavior or appearance updates

    For example, a custom highlight directive might:

    1. Be attached to an element.
    2. Listen for mouseenter.
    3. Apply a background style.
    4. Listen for mouseleave.
    5. 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:

    1. Components
    2. Attribute Directives
    3. Structural Directives

    Let's understand each one.

    1. Components

    A component is technically a specialized type of directive with its own template.

    For example:

    typescript
    @Component({
    selector: 'app-product-card',
    templateUrl: './product-card.component.html'
    })
    export class ProductCardComponent {}

    Used as:

    html
    <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:

    text
    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:

    • ngClass
    • ngStyle

    Custom directives are also frequently created as attribute directives.

    Example:

    html
    <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:

    typescript
    export class ProductComponent {
    isAvailable = true;
    }

    Template:

    html
    <div [ngClass]="{
    'available': isAvailable,
    'unavailable': !isAvailable
    }">
    Product Status
    </div>

    If:

    typescript
    isAvailable = true;

    Angular applies:

    text
    available

    If the value changes to false, Angular applies:

    text
    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:

    text
    SUCCESS
    PENDING
    FAILED

    You may want each status to have a different visual appearance.

    Conceptually:

    html
    <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:

    typescript
    export class ProductComponent {
    stock = 5;
    }

    Template:

    html
    <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:

    html
    <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:

    text
    *ngIf
    *ngFor
    *ngSwitchCase

    For example:

    html
    <div *ngIf="isLoggedIn">
    Welcome back!
    </div>

    Angular conditionally creates this part of the view.

    Another example:

    html
    <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:

    text
    @if
    @for
    @switch

    For example:

    html
    @if (isLoggedIn) {
    <p>Welcome back!</p>
    } @else {
    <p>Please log in.</p>
    }

    For lists:

    html
    @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:

    text
    Traditional Structural Directives
    ├── *ngIf
    ├── *ngFor
    └── *ngSwitch
    Modern 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.

    text
    Element Exists
    Directive Applied
    Behavior / Appearance Changes

    Example:

    html
    <p appHighlight>Important</p>

    Structural Directive

    Traditionally changes the structure of the rendered view.

    text
    Condition / Collection
    Structural Directive
    Template Instantiated
    DOM Structure Changes

    Example:

    html
    <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:

    bash
    ng generate directive highlight

    Shortcut:

    bash
    ng g d highlight

    A simplified directive may look like:

    typescript
    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:

    html
    <p appHighlight>
    This content is highlighted.
    </p>

    Angular detects:

    text
    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:

    typescript
    element.nativeElement.style

    Angular applications can use Renderer2.

    Example:

    typescript
    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.

    typescript
    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:

    text
    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:

    html
    <p [appHighlight]="'lightblue'">
    Important Information
    </p>

    Conceptually, the directive could accept an input:

    typescript
    @Input() appHighlight = 'yellow';

    Now different components can reuse the same directive with different configurations.

    text
    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.

    text
    User
    Authentication Service
    User Roles
    Role-Aware UI Directive
    ├── Admin -> Display Admin Control
    └── Customer -> Do Not Display Admin Control

    A template might conceptually use:

    html
    <button *appHasRole="'ADMIN'">
    Delete Account
    </button>

    This keeps repetitive UI authorization-display logic centralized.

    However, remember:

    text
    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 behavior
    Features
    Use directives across pages and workflows
    Shared UI
    Holds reusable UI primitives and directives
    Core Services
    Provide auth, config, analytics, and app state
    Tooltip Directive
    Autofocus Directive
    Permission UI Directive

    Shared directives can provide consistent behavior across the entire application.

    For example:

    text
    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
    • Email
    • 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:

    text
    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.

    text
    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 Renderer2 when 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.

    7 questions
    1BeginnerQuestionWhat is a directive in Angular?+

    Answer

    A directive is an Angular class that adds reusable behavior to elements or participates in controlling how template content is rendered.
    2BeginnerQuestionWhat are the main types of directives?+

    Answer

    Angular directives are commonly categorized as Components, Attribute Directives, and Structural Directives. A Component is a specialized directive with its own template.
    3IntermediateQuestionWhat is the difference between an Attribute Directive and a Structural Directive?+

    Answer

    An Attribute Directive changes the appearance or behavior of an existing element. A Structural Directive traditionally changes the rendered template structure by conditionally creating, removing, or repeating template fragments.
    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

    A custom directive is useful when the same reusable DOM or UI behavior needs to be applied across multiple elements or components. Examples include autofocus, click-outside detection, tooltips, and reusable interaction behavior.
    6AdvancedQuestionShould role-based UI directives be considered a security mechanism?+

    Answer

    No. They can control what users see in the frontend, but true authorization must always be enforced by backend services. Frontend restrictions alone can be bypassed.
    7AdvancedQuestionWhen would you choose a component instead of a directive?+

    Answer

    Use a component when the feature represents an independent piece of UI with its own template and behavior. Use an attribute directive when you primarily want to add reusable behavior to an existing element.

    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:

    text
    Component
    └── Creates a reusable UI with its own view
    Attribute Directive
    └── Adds behavior to an existing element
    Structural Directive
    └── Traditionally controls template structure
    Modern 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.

    Ready to mark this lesson complete?Track your journey across the entire course.