假设我有dynamic-component-wrapper
可以实例化传递给它的任何Component
类。
// DRE013 DCOOKE 16/05/2017 - The component to instantiate.
@Input() componentType: Type<{}>;
// DRE013 DCOOKE 16/05/2017 - the component that will be created
private _cmpRef: ComponentRef<Component>;
// DRE013 DCOOKE 16/05/2017 - Creates a component ref in the view at #target
createComponent(){
let factory = this.componentFactoryResolver.resolveComponentFactory(this.componentType);
this._cmpRef = this.target.createComponent(factory);
//this._cmpRef.instance.inputs >>>>>>>>> this is a string[] and I do not see how I can use this
}
使用示例
<shared-dynamic-component [componentType]="TestComponent"></shared-dynamic-component>
其中
TestComponent = TestComponent //class
这可以按预期工作,我可以在dynamic-component-wrapper
内收到此组件的实例,如下所示:
this._cmpRef.instance
此instance
对象上的Angular docs不明确 - 只是声明该实例的类型为C
- 绝对没有引用C的实际内容。
谢天谢地,我的IDE能够告诉我:
ComponentRef.instance
具有以下属性:
inputs : string[]
outputs : string[]
但是我不明白我是如何使用这些信息的。我想这只是@Input
字段的名称 - 但我想不出如何将复杂对象作为输入传递。
在使用@Inputs
动态创建组件后,我是否可以设置componentFactoryResolver
和其他元数据?
答案 0 :(得分:5)
这似乎不可能。这是解释原因。
对于每个组件,Angular编译器都会创建一个工厂。您可以在sources
文件夹下的ng://
标签中观察所有工厂,here is the example看看它们的外观。在创建工厂时,它定义将在此组件视图中呈现的nodes
。一些节点是子组件。
在生成此工厂时,它定义了框架应跟踪哪些输入属性。因此,除非您在编译之前定义输入属性,否则Angular将不会跟踪和更新输入。
以下是此工厂的示例:
function View_AppComponent_0(l) {
return jit_viewDef2(0,[
jit_queryDef3(201326592,1,{someComp: 0}),
(l()(),jit_elementDef4(0,null,null,1,'h1',[],null,null,null,null,null)),
(l()(),jit_textDef5(null,[
'Hello ',
''
]
)),
(l()(),jit_textDef5(null,['\n\n'])),
(l()(),jit_elementDef4(0,null,null,1,'b-comp',[],null,null,null,jit_View_BComponent_06,jit__object_Object_7)),
*************************************
// this is a child component's node definition which has `some` property binding
jit_directiveDef8(24576,null,0,jit_BComponent9,[],{some: [
0,
'some' <------------------------ property name to track
]
},null)
**************************************************
]
当您使用this.componentFactoryResolver.resolveComponentFactory
时,它实际上会搜索该工厂。它没有编译任何东西。
这当然有可能。您可以从父组件查询子组件并更新属性 - 它将在子组件的模板中正确呈现。你甚至可以触发onChanges生命周期钩子。
export class AppComponent {
@ViewChild(BComponent) bc;
ngAfterViewInit() {
setTimeout(() => {
this.bc.some = 'foo';
const changes = {some: new SimpleChange('', 'foo', true)};
this.bc.ngOnChanges(changes);
})
}
答案 1 :(得分:0)
我最终创建了一个类来这样做......以防它对其他人有所帮助。
const componentChangeBuilder = new DynamicComponentChangeBuilder(componentRef);
componentChangeBuilder.addInput('inputName', 'some string value');
componentChangeBuilder.addInput('someOtherInputName', 1234);
componentChangeBuilder.commit();
import { ComponentRef, OnChanges, SimpleChange, SimpleChanges } from '@angular/core';
/**
* Use this to track and trigger ngOnChanges for dynamically loaded components.
*/
export class DynamicComponentChangeBuilder<TComponent = any> {
private readonly componentRef: ComponentRef<TComponent>;
private readonly inputs = new Map<keyof TComponent, TComponent[keyof TComponent]>();
private readonly changesHistory = {} as SimpleChanges;
constructor(componentRef: ComponentRef<TComponent>) {
this.componentRef = componentRef;
}
public addInput(
input: keyof TComponent,
value: TComponent[typeof input],
): void {
this.inputs.set(input, value);
}
public commit() {
const currentChanges = {} as SimpleChanges;
this.inputs.forEach((newValue, input) => {
const previousChange = this.changesHistory[input as string];
let currentChange: SimpleChange;
if (!!previousChange) {
currentChange = {
previousValue: previousChange.currentValue,
currentValue: newValue,
firstChange: false,
} as SimpleChange;
} else {
currentChange = {
currentValue: newValue,
firstChange: true,
} as SimpleChange;
}
this.changesHistory[input as string] = currentChange;
currentChanges[input as string] = currentChange;
this.componentRef.instance[input] = currentChange.currentValue;
});
if (implementsOnChanges(this.componentRef.instance)) {
this.componentRef.instance.ngOnChanges(currentChanges);
}
}
}
function implementsOnChanges(component: any): component is OnChanges {
return (component as OnChanges)?.ngOnChanges !== undefined;
}