Angular 动态组件加载器-解析组件的共享服务
我必须使用动态添加组件。 例如,我有一个模式窗口,在按下视图上的某个按钮后动态加载该窗口。 此操作的代码如下所示:Angular 动态组件加载器-解析组件的共享服务,angular,typescript,angular2-services,dynamic-loading,Angular,Typescript,Angular2 Services,Dynamic Loading,我必须使用动态添加组件。 例如,我有一个模式窗口,在按下视图上的某个按钮后动态加载该窗口。 此操作的代码如下所示: let componentFactory = this.componentFactoryResolver.resolveComponentFactory(XYZComponent); let viewContainerRef = this.containerHost.viewContainerRef; viewContainerRef.clear(); let componentR
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(XYZComponent);
let viewContainerRef = this.containerHost.viewContainerRef;
viewContainerRef.clear();
let componentRef = viewContainerRef.createComponent(componentFactory);
let _XYZComponent = componentRef.instance as XYZComponent;
_XYZComponent.close.subscribe(() => {
componentRef.destroy();
});
export class SomeComponentThatRendersTheModal() {
renderFooComponent() {
// I don't know how you call your modal, so I'll just assume there's a modal service or whatever
this.modalService.openModal();
this.modalService.createComponent(
'FooComponent', {
inputs : { fooInputTest : 'kitten' },
outputs : { fooOutputTest : handleOutput }
}
);
}
// You can pass this method as the subscription `next` handler
handleOutput(emittedEvent) {
// ...
}
}
@Component({
selector: 'foo',
template: `
<h1>Foo Component, here's the input: " {{ fooInputTest }} "</h1>
<button (click)="fooOutputTest.emit('Greetings from foo')">Foo output test</button>
`
})
export class FooComponent {
@Input()
fooInputTest: any;
@Output()
fooOutputTest: EventEmitter<any> = new EventEmitter<any>();
}
每次我想使用这个模式窗口时,我都需要输入类似的代码,只是使用不同的组件名称
我想为此创建一个共享服务,但我找不到一个好的解决方案,我是说动态加载的组件
您知道如何创建一个好的服务来使用此代码吗?因为您将动态加载组件,所以您必须在某个地方注册这些组件,该位置位于模态组件装饰器的
entryComponents
中。此外,由于这些组件是typescriptclass
es,因此您需要从调用模态的任何组件导入它们。为了在一个地方处理这个问题,我们将把它们导入到一个文件中,然后在数组中导出它们
因此,在这里您将保留所有可能的动态组件,让我们调用此文件dynamic components.ts
:
import { FooComponent } from './path/to/foo';
import { BarComponent } from './path/to/bar';
import { BazComponent } from './path/to/baz';
// export all dynamic components
export const dynamicComponents = [
FooComponent, BarComponent, BazComponent
]
然后在模态组件中,可以将这些组件扩展到entryComponents
属性中
import { dynamicComponents } from './path/to/dynamic-components';
@Component({
//...
entryComponents: [ ...dynamicComponents ]
})
export class ModalComponent {}
到目前为止,你应该都知道
现在,在模态组件内部,您可以创建一个方法,该方法将以组件名称和一些元数据作为参数呈现组件,以动态处理props,我所说的props是指@Input()
和@Output()
装饰属性。这将使模态更加灵活,因为您将能够使用不同的输入和输出渲染组件
因此,您不必像现在这样在方法中硬编码组件,而必须从dynamicComponents
数组中提取它
由于Javascript类是函数的糖类语法,所有非匿名函数都有一个name
属性,这样您就可以将函数提供的name参数与dynamicComponents
中组件的名称相匹配
export class ModalComponent {
//...
createComponent(name: string, metadata: any) {
const viewContainerRef = this.entryContainer.viewContainerRef;
const cmpClass = dynamicComponents.find(cmp => cmp.name === name);
const cmpToCreate = new DynamicComponent(cmpClass, metadata);
const componentFactory = this.cmpFactoryResolver.resolveComponentFactory(cmpToCreate.component)
viewContainerRef.clear();
const cmpRef = viewContainerRef.createComponent(componentFactory);
// patch input values ...
for ( let input in metadata.inputs ) {
if ( metadata.inputs.hasOwnProperty(input) ) {
cmpRef.instance[input] = metadata.inputs[input];
}
}
// and subscribe to outputs
for ( let output in metadata.outputs ) {
if ( metadata.outputs.hasOwnProperty(output) ) {
console.log('hasOuput', metadata.outputs[output]);
cmpRef.instance[output].subscribe(metadata.outputs[output]);
}
}
}
}
有几件事要提。下面是DynamicComponent
类的定义:
export class DynamicComponent {
constructor(public component: Type<any>, data: any) {}
}
其中FooComponent
是这样的:
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(XYZComponent);
let viewContainerRef = this.containerHost.viewContainerRef;
viewContainerRef.clear();
let componentRef = viewContainerRef.createComponent(componentFactory);
let _XYZComponent = componentRef.instance as XYZComponent;
_XYZComponent.close.subscribe(() => {
componentRef.destroy();
});
export class SomeComponentThatRendersTheModal() {
renderFooComponent() {
// I don't know how you call your modal, so I'll just assume there's a modal service or whatever
this.modalService.openModal();
this.modalService.createComponent(
'FooComponent', {
inputs : { fooInputTest : 'kitten' },
outputs : { fooOutputTest : handleOutput }
}
);
}
// You can pass this method as the subscription `next` handler
handleOutput(emittedEvent) {
// ...
}
}
@Component({
selector: 'foo',
template: `
<h1>Foo Component, here's the input: " {{ fooInputTest }} "</h1>
<button (click)="fooOutputTest.emit('Greetings from foo')">Foo output test</button>
`
})
export class FooComponent {
@Input()
fooInputTest: any;
@Output()
fooOutputTest: EventEmitter<any> = new EventEmitter<any>();
}
@组件({
选择器:“foo”,
模板:`
Foo组件,下面是输入:“{{foointest}”
输出测试
`
})
导出类组件{
@输入()
食物输入测试:任何;
@输出()
fooOutputTest:EventEmitter也是。希望有帮助
2007年12月14日编辑:
显然,访问函数
对象的名称
属性并不能真正用于生产(您将得到一个无组件工厂发现错误),因为在对代码进行了丑化之后,函数的名称会被破坏,并且不匹配。在解释解决此问题的一些解决方法时,出现了一个问题注释。这是针对模态的子组件的,对吗?@OsmanCea是的,你是对的。我以前也做过类似的事情,但在尝试发布答案之前,我想知道一些事情。你感觉如何你在设计你的模态?你想在整个应用程序中有一个模态组件的实例吗?或者你想跨越多个实例,然后动态创建相应模态的内容?@OsmanCea我的应用程序中有一个模态组件的实例。好的,谢谢,我只需要知道这些。我会发布到在接下来的一两个小时内给我一个答案。