Как избавиться от ComponentFactoryResolver?
У меня есть сервис для модального окна и он успешно работает (рендерит компонент ModalComponent в конце тега body). Моя проблема заключается в том, что Angular говорит про ComponentFactoryResolver:
EN: Angular no longer requires Component factories. Please use other APIs where Component class can be used directly.
RU: Angular больше не требует фабрик компонентов. Используйте другие API, в которых класс компонента можно использовать напрямую
А я не понимаю, как мне ModalComponent рендерить в конце тега body, без использования ComponentFactoryResolver
import { Injectable, ComponentFactoryResolver, ComponentRef, Type, ApplicationRef, Injector } from '@angular/core';
import { ModalComponent } from './modal.component';
import { take, timer } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class ModalService {
private modalComponentRef: ComponentRef<ModalComponent> | null = null;
constructor(
private readonly componentFactoryResolver: ComponentFactoryResolver,
private readonly appRef: ApplicationRef,
private readonly injector: Injector
) {
}
public open(component: Type<any>): void {
if (this.modalComponentRef === null) {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent);
this.modalComponentRef = componentFactory.create(this.injector);
this.appRef.attachView(this.modalComponentRef.hostView);
document.body.appendChild(this.modalComponentRef.location.nativeElement);
}
this.modalComponentRef.instance.componentModalContent = component;
this.modalComponentRef.instance.openModal();
this.modalComponentRef.instance.close
.pipe(take(1))
.subscribe(() => this.close());
}
public close(): void {
if (this.modalComponentRef === null) return;
const transitionDurationProp = document.documentElement.computedStyleMap().get('--transitionDurationS') as CSSUnitValue;
const transitionDuration = transitionDurationProp.value || 0.2;
timer(transitionDuration * 1000)
.pipe(take(1))
.subscribe(() => {
if (this.modalComponentRef === null) return;
this.modalComponentRef.instance.closeModal();
this.appRef.detachView(this.modalComponentRef.hostView);
this.modalComponentRef.destroy();
this.modalComponentRef = null;
});
}
}
Ответы (2 шт):
Ты надо использовать ViewContainerRef. Ты можеи предоставить эго в конструкторе следующим образом:
construcor(vcr: ViewContainerRef) { }
После етого ты можеш использовать его следующим образом:
public open(component: Type<any>): void {
if (this.modalComponentRef === null) {
this.modalComponentRef = this.vcr.createComponent(component);
document.body.appendChild(this.modalComponentRef.location.nativeElement);
}
this.modalComponentRef.instance.componentModalContent = component;
this.modalComponentRef.instance.openModal();
this.modalComponentRef.instance.close
.pipe(take(1))
.subscribe(() => this.close());
Не уверён прям на все 100%, но, по описанию это должно работать вместо старой фабрики и не зависеть от конкретного компонента ангуляра.
Функция createComponent из @angular/core появилась начиная с версии 14.1
@Component({
standalone: true,
template: `Hello {{ name }}!`
})
class HelloComponent {
name = 'Angular';
}
@Component({
standalone: true,
template: `<div id="hello-component-host"></div>`
})
class RootComponent {}
// Инициализация приложения.
const applicationRef = await bootstrapApplication(RootComponent);
// Находим DOM-элемент, который будет использоваться в качестве хоста.
const hostElement = document.getElementById('hello-component-host');
// Получаем экземпляр `EnvironmentInjector` из `ApplicationRef`.
const environmentInjector = applicationRef.injector;
// Теперь мы можем создать экземпляр `ComponentRef`.
const componentRef = createComponent(HelloComponent, {hostElement, environmentInjector});
// Последний шаг — зарегистрировать вновь созданный компонент с помощью `ApplicationRef`,
// чтобы включить представление компонента в циклы обнаружения изменений.
applicationRef.attachView(componentRef.hostView);
componentRef.changeDetectorRef.detectChanges();