typescript 动态加载现有组件 Angular 2 Final Release
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39678963/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Load existing components dynamically Angular 2 Final Release
提问by Abner
I'm trying to load dynamically a component in the final release 2.0.0.
我正在尝试在最终版本 2.0.0 中动态加载一个组件。
Using RC5 I was loading using the following code:
使用 RC5 我正在使用以下代码加载:
Create a directive to load the controls:
创建一个指令来加载控件:
import {
CheckboxComponent, CheckboxListComponent,DatePickerComponent
} from '../components/';
@Directive({
selector: '[ctrl-factory]'
})
export class ControlFactoryDirective implements OnChanges {
@Input() model: any;
constructor(private vcRef: ViewContainerRef, private resolver: ComponentResolver) {
}
create(cp) {
this.resolver.resolveComponent(cp)
.then(factory => {
const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
this.vcRef.createComponent(factory, 0, injector, []);
let ch = this.vcRef.createComponent(factory, 0, injector, []).instance;
ch.model = this.model;
});
}
ngOnChanges() {
if (!this.model) return;
switch (this.model.type) {
case 'checkbox':
this.create(CheckboxComponent);
break;
case 'checkboxlist':
this.create(CheckboxListComponent);
break;
case 'datepicker':
this.create(DatePickerComponent);
break;
default:
break;
}
}
}
Then loaded that directive in my page like this:
然后在我的页面中加载该指令,如下所示:
<div ctrl-factory *ngFor="let child of page.childrens" [model]="child"></div>
But after updating from rc5 to 2.0.0 final release, the resolver doesn't exist anymore, was replaced by compiler.
但是从 rc5 更新到 2.0.0 最终版本后,解析器不再存在,被编译器取代。
I found loads of places showing how to load it using different codes, but all those too complex and I couldn't make it work.
我发现很多地方展示了如何使用不同的代码加载它,但所有这些都太复杂了,我无法让它工作。
Take this for instance: How can I use/create dynamic template to compile dynamic Component with Angular 2.0?
以这个为例:如何使用/创建动态模板来编译带有 Angular 2.0 的动态组件?
It looks more specific to that scenario, my one I just need to load the component and set an @Input called model.
它看起来更适合那个场景,我的那个我只需要加载组件并设置一个名为模型的@Input。
One thing when I was trying I had to create dynamically a module for each component, then add the component to it. But then I had issues saying that the component was being set in more than one Module, try to remove in some place an not working.
当我尝试时,我必须为每个组件动态创建一个模块,然后将组件添加到其中。但是后来我遇到了一个问题,说该组件被设置在多个模块中,尝试在某个地方删除但不起作用。
The major part of the code shown, I get from this link: http://blog.lacolaco.net/post/dynamic-component-creation-in-angular-2-rc-5/
显示的代码的主要部分,我从这个链接得到:http: //blog.lacolaco.net/post/dynamic-component-creation-in-angular-2-rc-5/
And did a couple of changes.
并做了一些改变。
Update
更新
I manage to make it work, using the following approach:
我设法使其工作,使用以下方法:
The create method has been changed to
创建方法已更改为
private create(cp) {
@NgModule({
imports: [BrowserModule, ControlsModule],
declarations: []
})
class DynamicModule {}
this.compiler.compileModuleAndAllComponentsAsync(DynamicModule)
.then(({componentFactories}) => {
const compFactory = componentFactories.find(x => x.componentType === cp);
const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
const cmpRef = this.vcRef.createComponent(compFactory, 0, injector, []);
cmpRef.instance.model = this.model;
});
}
Most places I've found, set create the Component and set it to the DynamicModule, the issue with that is when you are already declaring that same component in a different module, angular is going to complain about. The solution in my case was to import the my ControlsModule that has all my controls being exported.
我发现的大多数地方,设置创建组件并将其设置为 DynamicModule,问题是当您已经在不同的模块中声明相同的组件时,angular 会抱怨。在我的情况下,解决方案是导入我的 ControlsModule,其中所有控件都被导出。
回答by yurzui
Update
更新
NgComponentOutletwas introduced in 4.0.0-beta.3https://github.com/angular/angular/commit/8578682
NgComponentOutlet在4.0.0-beta.3 中引入https://github.com/angular/angular/commit/8578682
Coming soon NgComponentOutlet
I see two options to do that:
我看到两个选项可以做到这一点:
1) Using ComponentFactoryResolver.
1) 使用ComponentFactoryResolver。
It uses the already generated factory and the code looks something like this:
它使用已经生成的工厂,代码如下所示:
constructor(private vcRef: ViewContainerRef, private resolver: ComponentFactoryResolver) { }
create(comp) {
const factory = this.resolver.resolveComponentFactory(comp);
const compRef = this.vcRef.createComponent(factory);
(<any>compRef).instance.model = this.model;
}
In this case we have to define dynamic component in declarations
and entryComponents
properties within decorator of module
在这种情况下,我们必须在模块的装饰器中定义动态组件declarations
和entryComponents
属性
@NgModule({
imports: [ BrowserModule ],
declarations: [ AppComponent, DynamicComponent ],
entryComponents: [DynamicComponent],
bootstrap: [ AppComponent ]
})
export class AppModule { }
2) Using Compiler
2) 使用编译器
In this case we can only run module compilation by using compiler.compileModuleAndAllComponentsAsync
and then find component from componentFactories array. Your directive might look like this:
在这种情况下,我们只能通过 using 运行模块编译compiler.compileModuleAndAllComponentsAsync
,然后从 componentFactories 数组中查找组件。您的指令可能如下所示:
constructor(private vcRef: ViewContainerRef, private loader: DynamicLoaderService) { }
create(comp) {
this.loader.createComponentFactory(comp).then(factory => {
const compRef = this.vcRef.createComponent(factory);
(<any>compRef).instance.model = this.model;
})
}
DynamicLoaderService
is a global service that will load and store component factories.
DynamicLoaderService
是一个全局服务,将加载和存储组件工厂。
@Injectable()
export class DynamicLoaderService {
constructor(protected compiler: Compiler) {}
private resolveCompHelper$ = new Subject<any>();
private cache = new Map<string, ComponentFactory<any> | number>();
public createComponentFactory(type: string) : Promise<ComponentFactory<any>> {
let factory = this.cache.get(type);
// if factory has been already loading
if(factory === 1) {
return new Promise((resolve) => {
// waiting compilation of factory
const subscriber = this.resolveCompHelper$.subscribe((data) => {
if(type !== data.type) return;
subscriber.unsubscribe();
resolve(data.factory);
});
});
}
// factory exists in cache
if (factory) {
return new Promise((resolve) => resolve(factory));
}
const comp = typeMap[type];
// factory startes loading
this.cache.set(type, 1);
return new Promise((resolve) => {
this.compiler.compileModuleAndAllComponentsAsync(createComponentModule(comp))
.then((moduleWithFactories: ModuleWithComponentFactories<any>) => {
factory = moduleWithFactories.componentFactories
.find(x => x.componentType === comp);
this.cache.set(type, factory);
this.resolveCompHelper$.next({ type, factory});
resolve(factory);
});
});
}
}
Hope it helps you!
希望对你有帮助!
回答by Mateo Tibaquira
It was useful for me to see this option based on a Directive, but I found the one that fit my needs based on Components: https://www.ag-grid.com/ag-grid-angular-aot-dynamic-components/
根据指令查看此选项对我很有用,但我找到了一个适合我基于组件的需求的选项:https: //www.ag-grid.com/ag-grid-angular-aot-dynamic-components /
Happy coding!
快乐编码!