By Jerry Gagliano

Created on:

Updated on:

Angular

With Angular version 15, a particular feature I want to share is the directive composition api and how we can implement it.

This feature allows you to create a standalone directive without the need to be rendered in the template. Developers were demanding this feature which I had no idea which is good to see. Here is an article for more details Directive composition API

An interesting concept about this feature is using the takeUntil pattern with a directive. For those of you who don't know about this particular pattern, it's basically using a Subject observable. When the component is destroyed, the observable is emitted and unsubscribes the observable. This is a common pattern used in Angular app development but not in a well-structured way.

Here is an example without the directive composition API:


  @Component()
  export class YourComponentName implements OnDestroy {
    private destroy$ = new Subject<void>();

    constructor() {
      AnyObservable$.pipe(
        takeUntil(this.destroy$)
      ).subscribe((params) => {
        ...your code here
      });
    }

    ngOnDestory() {
      this.destroy$.next();
    }
  }

As we see, the observable will unsubscribe once the component is destroyed and will emit the Subject observable. If we had hundreds of components that implement this specific pattern, lots of code would be repeated is not ideal.

Here is an example of using directive composition API composing with a directive:


   export class DirectiveName implements OnDestroy {
    private destroy$ = new Subject<void>();

    get pipe() {
      return pipe(takeUntil(this.destroy$));
    }

    ngOnDestory() {
      this.destroy$.next();
    }
  }

  @Directive({ standalone: true }) // standalone is needed
  export class DirectiveName implements OnDestroy {
    private destroy$ = new Subject<void>();

    get pipe() {
      return pipe(takeUntil(this.destroy$));
    }

    ngOnDestory() {
      this.destroy$.next();
    }
  }

Here we import the directive using the hostDirectives property


  @Component({
    hostDirectives: [DestroyedDirective]
  })
  export class ComponentName implements OnInit {
    private untilDestroyed = inject(DestroyedDirective).pipe;

    constructor() {
      AnyObservable$.pipe(
        this.untilDestroyed
      ).subscribe(() => {
        ...will have code here
      });
    }
  }

As we see much cleaner and code is centralized. If we need to modify the takeUntil operator or add logic we would have one common file.

One argument we might have, directives are UI based elements only. This is true, but you're still actually managing functionality in the component UI. The other argument is why not just use a service? I prefer not to use services much. Services are used mainly to create methods containing endpoint calls or without UI component interaction. If you use a state management library like NGXS, services are much less needed.

Final Thoughts

A quick example of how to structure your takeUntil's using the new directive composition API. This is just one idea and I am sure other developers will implement this feature in different ways. If other developers have other ideas, please email me with examples as I am curious to expand my knowledge about this feature.

Cheers!

Thank you for taking the time in reading this blog. If you find any invalid information or want to add more this blog you may contact me at:

jerrygag@gmail.com