В Angular v15 мы можем использовать новый API композиции директив, чтобы применять директивы непосредственно к элементу host компонента. Создание пользовательских условных структурных директив на данный момент требует немного шаблонной работы. Давайте посмотрим, как мы можем использовать его для создания более чистых пользовательских директив ngIf.

Предположим, что нам нужно переключить некоторые представления на основе проекта status пользователя. Как хорошие разработчики, мы создадим повторно используемую директиву с именем ShowIfProjectActiveDirective:

import { NgIf } from '@angular/common';
import { Directive, inject, Input } from '@angular/core';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';

@UntilDestroy()
@Directive({
  selector: '[showIfProjectActive]',
  hostDirectives: [{ 
     directive: NgIf, 
     inputs: ['ngIfElse: showIfProjectActiveElse'] },
  ],
  standalone: true,
})
export class ShowIfProjectActiveDirective {
  @Input('showIfProjectActive') showIfProjectActive: boolean;

  private ngIfDirective = inject(NgIf);
  private projectService = inject(ProjectService);

  ngOnInit() {
    this.projectService.active$
      .pipe(untilDestroyed(this))
      .subscribe((isActive) => {
        this.ngIfDirective.ngIf = this.showIfProjectActive
          ? isActive
          : !isActive;
      });
  }
}

Функция hostDirectives используется для создания экземпляра директивы NgIf и применения ее к нашему элементу host. Чтобы установить свойство ngIf, мы получаем ссылку на экземпляр директивы NgIf и устанавливаем его вручную на основе нашей логики active.

Кроме того, мы хотим, чтобы потребители могли использовать встроенный ввод ngIfElse, поэтому мы предоставляем ввод от NgIfDirective под псевдонимом showIfProjectActiveElse.

Теперь мы можем использовать нашу директиву в шаблоне:

<button *showIfProjectActive="true; else elseTpl">Foo</button>

<ng-template #elseTpl>Bar</ng-template>

<!-- Or use the ng-template version -->
<ng-template [showIfProjectActive]="true" 
             [showIfProjectActiveElse]="elseTpl">
  Foo
</ng-template>

Вот еще один пример представления на основе ролей:

import { NgIf } from '@angular/common';
import { Directive, inject, Input } from '@angular/core';

@Directive({
  selector: '[ifHasRole]',
  hostDirectives: [{ 
     directive: NgIf, 
     inputs: ['ngIfElse: ifHasRoleElse'] 
  }],
  standalone: true,
})
export class IfHasRoleDirective {
  private ngIfDirective = inject(NgIf);
  private authorizationService = inject(AuthorizationService);

  @Input('ifHasRole') set role(role: 'admin' | 'user') {
    this.ngIfDirective.ngIf = this.authorizationService.hasRole(role)
  }
}

И используйте его в шаблоне:

<button *ifHasRole="'admin'; else elseTpl">Foo</button>

<ng-template #elseTpl>Bar</ng-template>

Подпишитесь на меня в Medium или Twitter, чтобы узнать больше об Angular и JS!