angular
June 4

Использование функции createComponent() в Angular

Angular v14.1 добавил новую функцию createComponent. Эта функция может заменить заменяет ComponentFactory, который использовался в предыдущих версиях. Функция позволяет создавать экземпляр ComponentRef на основе предоставленного компонента и набора параметров:

import { ApplicationRef, createComponent, EnvironmentInjector } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class ToastService {

  constructor(
    private appRef: ApplicationRef,
    private injector: EnvironmentInjector
  ) { }

  open(text: string) {
    // Create a `ComponentRef` instance.
    const dialogRef = createComponent(ToastComponent, {
      environmentInjector: this.injector
    });

    dialogRef.setInput('text', text);

    document.body.appendChild(dialogRef.location.nativeElement);

    // Register the newly created ref using the `ApplicationRef` instance
    // to include the component view into change detection cycles.
    this.appRef.attachView(dialogRef.hostView);
  }
}

Можно передать новый environment injector, унаследованный от текущего:

export class ToastService {

  constructor(
    private appRef: ApplicationRef,
    private injector: EnvironmentInjector
  ) { }

  open(text: string) {
    const newEnvInjector = createEnvironmentInjector([
      {
        provide: 'MyToken',
        useValue: 'Token'
      }
    ], this.injector);

    const dialogRef = createComponent(ToastComponent, {
      environmentInjector: newEnvInjector
    });

    dialogRef.setInput('text', text);

    document.body.appendChild(dialogRef.location.nativeElement);

    this.appRef.attachView(dialogRef.hostView);
  }
}

Так же можно создать element injector:

export class ToastService {

  constructor(
    private appRef: ApplicationRef,
    private injector: EnvironmentInjector
  ) { }

  open(text: string) {
    const elementInjector = Injector.create({
      providers: [
        {
          provide: 'MyToken',
          useValue: 'Token'
        }
      ],
    });

    const dialogRef = createComponent(ToastComponent, {
      environmentInjector: this.injector,
      elementInjector
    });

    dialogRef.setInput('text', text);

    document.body.appendChild(dialogRef.location.nativeElement);

    this.appRef.attachView(dialogRef.hostView);
  }
}

Компонент может быть добавлен к элементу-хосту:

export class ToastService {

  constructor(
    private appRef: ApplicationRef,
    private injector: EnvironmentInjector
  ) { }

  open(text: string) {
    const dialogRef = createComponent(ToastComponent, {
      environmentInjector: this.injector,
      hostElement: document.getElementById('toasts-container')!
    });

    dialogRef.setInput('text', text);

    this.appRef.attachView(dialogRef.hostView);
  }
}

Мы можем передать projectableNodes — список узлов DOM, которые должны быть проецированы через <ng-content> нового экземпляра компонента:

  openDialog<T>(dialogContent: Type<T>) {
    const footer = document.createElement('p');
    footer.innerText = 'footer';
    
    const dialogContentRef = createComponent(dialogContent, { 
      environmentInjector: this.injector 
    })

    const dialogRef = createComponent(DialogComponent, {
      environmentInjector: this.injector,
      hostElement: document.getElementById('dialog-container')!,
      projectableNodes: [
        // ng-content nodes
        [dialogContentRef.location.nativeElement],
        // second ng-content (e.g <ng-content select="footer"></ng-content>)
        [ footer ]
      ]
    })

    this.appRef.attachView(dialogRef.hostView)
 }

Метод createComponent в Angular используется для динамического создания экземпляров компонентов во время выполнения приложения. Это полезно, например, для создания модальных окон, динамической загрузки данных или интеграции с библиотеками сторонних разработчиков.