How to generate a directive in Angular

Generating Angular directives enables you to create reusable DOM manipulation logic, custom behavior, and component enhancements that can be applied across your application. As the creator of CoreUI, a widely used open-source UI library, I’ve generated hundreds of Angular directives for tooltip behavior, input validation, accessibility features, and custom styling logic across enterprise component libraries. From my expertise, the most efficient approach is to use Angular CLI’s generate directive command. This method creates the proper directive structure, imports, and testing files while following Angular best practices for directive development and integration.

Use ng generate directive command to create a new Angular directive with proper structure and configuration.

# Generate a basic directive
ng generate directive highlight
# or shortened
ng g d highlight

# Generate directive in specific folder
ng generate directive shared/directives/tooltip

# Generate directive with custom selector
ng generate directive my-custom --selector="[customBehavior]"

# Generate directive without test file
ng generate directive highlight --skip-tests

# Generate directive in a specific module
ng generate directive highlight --module=shared

# Generate directive with flat structure (no separate folder)
ng generate directive highlight --flat
// highlight.directive.ts (generated file)
import { Directive, ElementRef, HostListener, Input, Renderer2 } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  @Input() highlightColor = 'yellow';
  @Input() defaultColor = 'transparent';

  constructor(
    private el: ElementRef,
    private renderer: Renderer2
  ) {}

  @HostListener('mouseenter') onMouseEnter() {
    this.highlight(this.highlightColor || 'yellow');
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.highlight(this.defaultColor);
  }

  private highlight(color: string) {
    this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', color);
  }
}
// Advanced directive example - tooltip.directive.ts
import {
  Directive,
  ElementRef,
  HostListener,
  Input,
  Renderer2,
  ComponentRef,
  ViewContainerRef,
  ComponentFactoryResolver
} from '@angular/core';

@Directive({
  selector: '[appTooltip]'
})
export class TooltipDirective {
  @Input('appTooltip') tooltipText = '';
  @Input() placement: 'top' | 'bottom' | 'left' | 'right' = 'top';
  @Input() delay = 500;

  private tooltipComponent: ComponentRef<any> | null = null;
  private timer: any;

  constructor(
    private el: ElementRef,
    private renderer: Renderer2,
    private viewContainer: ViewContainerRef,
    private componentFactory: ComponentFactoryResolver
  ) {}

  @HostListener('mouseenter') onMouseEnter() {
    this.timer = setTimeout(() => {
      this.showTooltip();
    }, this.delay);
  }

  @HostListener('mouseleave') onMouseLeave() {
    clearTimeout(this.timer);
    this.hideTooltip();
  }

  private showTooltip() {
    if (this.tooltipComponent || !this.tooltipText) {
      return;
    }

    // Create tooltip element
    const tooltip = this.renderer.createElement('div');
    this.renderer.addClass(tooltip, 'tooltip');
    this.renderer.addClass(tooltip, `tooltip-${this.placement}`);
    this.renderer.setProperty(tooltip, 'textContent', this.tooltipText);

    // Position tooltip
    this.renderer.appendChild(document.body, tooltip);
    this.positionTooltip(tooltip);

    // Store reference for cleanup
    this.tooltipComponent = { location: { nativeElement: tooltip } } as any;
  }

  private hideTooltip() {
    if (this.tooltipComponent) {
      this.renderer.removeChild(document.body, this.tooltipComponent.location.nativeElement);
      this.tooltipComponent = null;
    }
  }

  private positionTooltip(tooltip: HTMLElement) {
    const hostPos = this.el.nativeElement.getBoundingClientRect();
    const tooltipPos = tooltip.getBoundingClientRect();

    let top: number, left: number;

    switch (this.placement) {
      case 'top':
        top = hostPos.top - tooltipPos.height - 10;
        left = hostPos.left + (hostPos.width - tooltipPos.width) / 2;
        break;
      case 'bottom':
        top = hostPos.bottom + 10;
        left = hostPos.left + (hostPos.width - tooltipPos.width) / 2;
        break;
      case 'left':
        top = hostPos.top + (hostPos.height - tooltipPos.height) / 2;
        left = hostPos.left - tooltipPos.width - 10;
        break;
      case 'right':
        top = hostPos.top + (hostPos.height - tooltipPos.height) / 2;
        left = hostPos.right + 10;
        break;
    }

    this.renderer.setStyle(tooltip, 'position', 'fixed');
    this.renderer.setStyle(tooltip, 'top', `${top}px`);
    this.renderer.setStyle(tooltip, 'left', `${left}px`);
  }
}
// Usage in component template
@Component({
  template: `
    <!-- Basic highlight directive -->
    <p appHighlight>Hover over this text</p>

    <!-- With custom color -->
    <p appHighlight highlightColor="lightblue">Custom color highlight</p>

    <!-- Tooltip directive -->
    <button appTooltip="Click me to submit" placement="top">
      Submit Form
    </button>

    <!-- Multiple directives -->
    <div appHighlight appTooltip="This div has both directives" highlightColor="lightgreen">
      Hover for highlight and tooltip
    </div>
  `
})
export class MyComponent {}
/* Add styles for tooltip directive */
.tooltip {
  background-color: #333;
  color: white;
  padding: 8px 12px;
  border-radius: 4px;
  font-size: 14px;
  z-index: 1000;
  box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}

.tooltip-top::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  margin-left: -5px;
  border: 5px solid transparent;
  border-top-color: #333;
}

Angular CLI’s ng generate directive command creates the directive class with the @Directive decorator, proper selector, constructor with dependency injection, and optional test file. The directive class can use @Input() for configuration, @HostListener() for event handling, and dependency injection for services like ElementRef, Renderer2, and ViewContainerRef. Directives can be attribute directives (modify element behavior) or structural directives (modify DOM structure). Always use Renderer2 for DOM manipulation instead of direct element access for better security and server-side rendering compatibility.

Best Practice Note:

This is the same approach we use for generating directives in CoreUI Angular components for consistent behavior and DOM manipulation. Use Angular CLI for proper directive structure, leverage dependency injection for services, implement proper cleanup in ngOnDestroy, use Renderer2 for safe DOM manipulation, and test directives thoroughly with both unit and integration tests.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.
How to Migrate from create-react-app to Vite?
How to Migrate from create-react-app to Vite?

How to capitalize the first letter in JavaScript?
How to capitalize the first letter in JavaScript?

How to Add a Tab in HTML
How to Add a Tab in HTML

How to Use JavaScript setTimeout()
How to Use JavaScript setTimeout()

Answers by CoreUI Core Team