As Angular developers, we're constantly seeking ways to enhance our applications, streamline workflows, and deliver exceptional user experiences. While Angular provides a robust set of built-in directives, custom directives empower us to extend the framework's functionality to suit our specific needs. In this blog post, we'll explore the most useful essential custom directives that can take your Angular development to the next level.

1. Click Outside Directive


This directive detects clicks outside a specified element, useful for closing dropdowns, modals, or other UI components. Let's see how it's done:


import { Directive, ElementRef, Output, EventEmitter, HostListener } from '@angular/core';

@Directive({
  selector: '[appClickOutside]'
})
export class ClickOutsideDirective {
  @Output() clickOutside = new EventEmitter<void>();

  constructor(private elementRef: ElementRef) {}

  @HostListener('document:click', ['$event.target']) onClick(targetElement: HTMLElement) {
    const clickedInside = this.elementRef.nativeElement.contains(targetElement);
    if (!clickedInside) {
      this.clickOutside.emit();
    }
  }
}


Usage Example:


<div (clickOutside)="closeDropdown()">Dropdown Content</div>


2. Infinite Scroll Directive


Load more content as the user scrolls down the page, providing a seamless browsing experience. Here's how you can implement infinite scroll:


import { Directive, ElementRef, Output, EventEmitter, HostListener } from '@angular/core';


@Directive({
  selector: '[appInfiniteScroll]'
})
export class InfiniteScrollDirective {
  @Output() scrolled = new EventEmitter<void>();


  constructor(private elementRef: ElementRef) {}


  @HostListener('scroll', ['$event']) onScroll(event: Event): void {
    const target = event.target as HTMLElement;
    if (target.scrollTop + target.clientHeight >= target.scrollHeight) {
      this.scrolled.emit();
    }
  }
}


Usage Example:


<div (scrolled)="loadMoreItems()">Scrollable Content</div>


3. Drag and Drop Directive


Enables drag-and-drop functionality within a component, allowing users to reorder items or upload files by dragging them into designated areas.


import { Directive, HostListener, Output, EventEmitter } from '@angular/core';


@Directive({
  selector: '[appDragAndDrop]'
})
export class DragAndDropDirective {
  @Output() fileDropped = new EventEmitter<FileList>();

  constructor() {}

  @HostListener('drop', ['$event']) onDrop(event: DragEvent): void {
    event.preventDefault();
    const files = event.dataTransfer.files;
    if (files.length > 0) {
      this.fileDropped.emit(files);
    }
  }

  @HostListener('dragover', ['$event']) onDragOver(event: DragEvent): void {
    event.preventDefault();
  }
}


Usage Example:


<div appDragAndDrop (fileDropped)="onFileDropped($event)"> <!-- Drop zone content --> </div>


4. Equal Height Directive


Ensures that a group of elements have the same height, useful for creating grid layouts or aligning elements within a row.


import { Directive, ElementRef, AfterViewInit } from '@angular/core';

@Directive({
  selector: '[appEqualHeight]'
})
export class EqualHeightDirective implements AfterViewInit {
  constructor(private elementRef: ElementRef) {}

  ngAfterViewInit(): void {
    const elements = this.elementRef.nativeElement.children;
    const maxHeight = Math.max(...Array.from(elements).map((el: HTMLElement) => el.offsetHeight));
    Array.from(elements).forEach((el: HTMLElement) => el.style.height = `${maxHeight}px`);
  }
}


Usage Example:


<div appEqualHeight>
  <div>Content 1</div>
  <div>Content 2</div>
  <!-- Additional content -->
</div>


5. Scroll Animation Directive


Adds animations to elements when they come into view during scrolling, providing a visually appealing user experience.


import { Directive, ElementRef, Renderer2, HostListener } from '@angular/core';

@Directive({
  selector: '[appScrollAnimation]'
})
export class ScrollAnimationDirective {
  constructor(private elementRef: ElementRef, private renderer: Renderer2) {}

  @HostListener('window:scroll', []) onWindowScroll(): void {
    const scrollPosition = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
    const elementPosition = this.elementRef.nativeElement.offsetTop;
    const windowHeight = window.innerHeight;
    if (elementPosition < scrollPosition + windowHeight) {
      this.renderer.addClass(this.elementRef.nativeElement, 'animate');
    }
  }
}


Usage Example:


<div appScrollAnimation>
  <!-- Content to animate -->
</div>


6. Debounce Directive


Debounces user input to prevent rapid firing of events, reducing unnecessary processing and improving performance.


import { Directive, EventEmitter, Output } from '@angular/core';
import { Subject } from 'rxjs';
import { debounceTime } from 'rxjs/operators';

@Directive({
  selector: '[appDebounce]'
})
export class DebounceDirective {
  @Output() debounced = new EventEmitter<string>();

  private subject = new Subject<string>();

  constructor() {
    this.subject.pipe(debounceTime(300)).subscribe(value => this.debounced.emit(value));
  }

  @HostListener('input', ['$event.target.value']) onInput(value: string): void {
    this.subject.next(value);
  }
}


Usage Example:


<input type="text" appDebounce (debounced)="onDebouncedInput($event)">


Conclusion:


These custom directives offer powerful ways to extend the functionality and interactivity of your Angular applications. By leveraging these directives, you can streamline development, enhance user experiences, and build more sophisticated applications. Experiment with them, adapt them to your project's requirements, and unlock new possibilities in your Angular development journey!

Hope all of them helpful to you guys <3