如何在Angular中使用第三方库动态创建组件



使用Angular 10

SO上有很多类似的问题,但我还没有找到一个能回答我情况的问题。

我希望有人能指引我。

我正在使用第三方库来显示360°照片。这个第三方库有一个内置的API来显示场景中的热点。只需给库一个你想成为热点的元素,它就可以处理剩下的

我的大部分工作都如预期的那样,但也有几部分没有。

到目前为止,我正在动态生成我的组件,如下所示:

this._hotspotFactory = this.resolver.resolveComponentFactory(HotspotComponent);
const component = this._hotspotFactory.create(this.injector);
//Hydrate component with  bunch of data
component.instance.id = data.id;
...
// Create the Hotspot with Third Party
// Calling this third party method injects the native element into the DOM. 
// Passing the nativeElement in. Looks great at first glance. 
const hotspot = this._scene.createHotspot(data, component.location.nativeElement);
this.appRef.attachView(component.hostView);
component.hostView.detectChanges();
if(component.instance.over.observers.length) {
hotspot.on('over', (evt) => {
this.zone.run(() => {
component.instance.over.emit(evt);
});
});
}
if(component.instance.out.observers.length) {
hotspot.on('out', (evt) => {
this.zone.run(() => {
component.instance.out.emit(evt);
});
});
}
if(component.instance.navigate.observers.length) {
hotspot.on('click', (evt) => {
this.zone.run(() => {
component.instance.navigate.emit(evt);
})
});
}

没有抛出任何错误,我成功地看到了场景中应该出现的热点。甚至HotspotComponent模板中的数据插值也如预期的那样发生。

但是,[ngStyle]绑定从未在HotspotComponent中产生动态样式。

我有99%的把握,这是因为组件中没有进行更改检测。

我使用this.appRef.attachView(component.hostView)手动附加视图,因为第三方负责将元素注入DOM,而不是Angular。因此Angular需要了解它,以便执行更改检测。

即使手动调用attachView,我仍然认为Angular不知道视图中的这个组件,因为Angular Chrome扩展调试器没有在其开发工具中将其注册为视图中的已知组件。。。。尽管在屏幕和DOM中看到了它。

我错过了什么?

组件有什么变化检测策略?当一个组件被添加到视图中时,它的生命周期挂钩将由角度(ngOninitngAfterContentInit等(触发。在这些日志中记录一些内容,看看是否调用了主题生命周期挂钩。无论变更检测策略如何,在将组件添加到视图后,都应在组件上进行一个变更检测循环。

如果生命周期钩子调用没有发生,那么就意味着angular不参与将元素添加到DOM中。

angular似乎有一个生命周期挂钩,正好适合您的用例'ngDoBootstrap'

由于我们无法调试您的完整源代码,从您提到的信息来看,您试图附加到视图的动态组件似乎不可用于NgModule中的Angular角引导的每个组件都必须在NgModule中

尽管您可以使用'ngDoBootstrap'动态引导它。

它的使用方式如下:

ngDoBootstrap(appRef: ApplicationRef) {
this.fetchDataFromApi().then((componentName: string) => {
if (componentName === 'ComponentOne') {
appRef.bootstrap(ComponentOne);
} else {
appRef.bootstrap(ComponentTwo);
}
});
}

在您的情况下,可以在将构件附着到视图之前执行此操作。

...
appRef.bootstrap(component);
this.appRef.attachView(component.hostView);
component.hostView.detectChanges();
...

请查看此处的文档:https://angular.io/api/core/ApplicationRef

我们使用ComponentFactoryResolver类给出的resolveComponentFactory方法,该方法用于组件级延迟加载。为了确认您的组件确实被分成,请执行ng build --prod,否则您将看到为SoftwareListComponent生成的.js块。

app.component.html

<button (click)="loadSoftwareListDynamically()> Load </button>
<div #softwareListContainer></div>

应用程序组件.ts

constructor(
private componentFactoryResolver: ComponentFactoryResolver
) {}
@ViewChild('softwareListContainer', { read: ViewContainerRef })
softwareListContainer: ViewContainerRef;
loadSoftwareListDynamically() {
import('../common-features/software-list/software-list.component').then(
({ SoftwareListComponent }) => {
const componentFactory =
this.componentFactoryResolver.resolveComponentFactory(
SoftwareListComponent
);
this.softwareListContainer.createComponent(componentFactory);
}
);
}

软件列表模块.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [
SoftwareListComponent
],
imports: [
CommonModule,
RouterModule,
SwiperModule    <- External Library
],
})
export class SoftwareListModule {}

有关更多信息,您可以转到我对使用ComponentFactoryResolver创建的组件的懒惰加载的完整讨论。您将在此处获得更多信息->StackOverflow讨论

Stacklitz链接此处

最新更新