处理 Angular 2 中动态创建的组件的 @Input 和 @Output
Posted
技术标签:
【中文标题】处理 Angular 2 中动态创建的组件的 @Input 和 @Output【英文标题】:Handle @Input and @Output for dynamically created Component in Angular 2 【发布时间】:2017-03-15 21:42:59 【问题描述】:如何为 Angular 2 中动态创建的组件处理/提供 @Input
和 @Output
属性?
这个想法是在调用 createSub 方法时动态创建(在这种情况下)SubComponent。分叉很好,但我如何为 SubComponent 中的@Input
属性提供数据。另外,如何处理/订阅 SubComponent 提供的@Output
事件?
示例: (两个组件在同一个 NgModule 中)
应用组件
@Component(
selector: 'app-root'
)
export class AppComponent
someData: 'asdfasf'
constructor(private resolver: ComponentFactoryResolver, private location: ViewContainerRef)
createSub()
const factory = this.resolver.resolveComponentFactory(SubComponent);
const ref = this.location.createComponent(factory, this.location.length, this.location.parentInjector, []);
ref.changeDetectorRef.detectChanges();
return ref;
onClick()
// do something
子组件
@Component(
selector: 'app-sub'
)
export class SubComponent
@Input('data') someData: string;
@Output('onClick') onClick = new EventEmitter();
【问题讨论】:
过去你可以做这样的事情..ref.instance.someData = someData
。我不确定是否仍然如此。
这仍然有效。没有其他方法(除了使用共享服务进行通信)。 ***.com/questions/36325212/… 包含更多详细信息。
@KrisHollenbeck @Günter Zöchbauer 谢谢。所以每次父组件中的数据更改时,我必须将其设置为'ref.instance.someData = someData'并自行触发更改检测('ref.changeDetectorRef.detectChanges();')?以及如何找出子组件中的哪个属性是正确的?可以命名完全不同...:/也许直接使用Reflection
会尝试:)
@thpnk,是的,我相信是的,听起来不错。自从后来的 RC 版本之一以来,我还没有尝试过这样做。我会参考 Gunter Zochbauer 的那篇文章以获取更多信息。 ***.com/questions/36325212/…
【参考方案1】:
createSub()
const factory = this.resolver.resolveComponentFactory(SubComponent);
const ref = this.location.createComponent(factory, this.location.length,
ref.instance.model = Which you like to send
ref.instance.outPut = (data) => //will get called from from SubComponent
this.location.parentInjector, []);
ref.changeDetectorRef.detectChanges();
return ref;
SubComponent
public model;
public outPut = <any>;
constructor() console.log("Your input will be seen here",this.model)
sendDataOnClick()
this.outPut(inputData)
【讨论】:
【参考方案2】:如果您知道要添加的组件的类型,我认为您可以使用其他方法。
在您的应用根组件 html 中:
<div *ngIf="functionHasCalled">
<app-sub [data]="dataInput" (onClick)="onSubComponentClick()"></app-sub>
</div>
在您的应用根组件打字稿中:
private functionHasCalled:boolean = false;
private dataInput:string;
onClick()
//And you can initialize the input property also if you need
this.dataInput = 'asfsdfasdf';
this.functionHasCalled = true;
onSubComponentClick()
【讨论】:
【参考方案3】:为@Input 提供数据非常简单。您已将组件命名为 app-sub,并且它有一个名为 data 的 @Input 属性。可以通过这样做来提供这些数据:
<app-sub [data]="whateverdatayouwant"></app-sub>
【讨论】:
请先尝试理解问题【参考方案4】:我发现以下代码可以从字符串 (angular2 generate component from just a string) 动态生成组件,并从中创建了一个 compileBoundHtml 指令,该指令传递输入数据(不处理输出,但我认为同样的策略适用于你可以修改这个):
@Directive(selector: '[compileBoundHtml]', exportAs: 'compileBoundHtmlDirective')
export class CompileBoundHtmlDirective
// input must be same as selector so it can be named as property on the DOM element it's on
@Input() compileBoundHtml: string;
@Input() inputs?: [x: string]: any;
// keep reference to temp component (created below) so it can be garbage collected
protected cmpRef: ComponentRef<any>;
constructor( private vc: ViewContainerRef,
private compiler: Compiler,
private injector: Injector,
private m: NgModuleRef<any>)
this.cmpRef = undefined;
/**
* Compile new temporary component using input string as template,
* and then insert adjacently into directive's viewContainerRef
*/
ngOnChanges()
class TmpClass
[x: string]: any;
// create component and module temps
const tmpCmp = Component(template: this.compileBoundHtml)(TmpClass);
// note: switch to using annotations here so coverage sees this function
@NgModule(imports: [/*your modules that have directives/components on them need to be passed here, potential for circular references unfortunately*/], declarations: [tmpCmp])
class TmpModule ;
this.compiler.compileModuleAndAllComponentsAsync(TmpModule)
.then((factories) =>
// create and insert component (from the only compiled component factory) into the container view
const f = factories.componentFactories[0];
this.cmpRef = f.create(this.injector, [], null, this.m);
Object.assign(this.cmpRef.instance, this.inputs);
this.vc.insert(this.cmpRef.hostView);
);
/**
* Destroy temporary component when directive is destroyed
*/
ngOnDestroy()
if (this.cmpRef)
this.cmpRef.destroy();
重要的修改在于增加了:
Object.assign(this.cmpRef.instance, this.inputs);
基本上,它会将您希望在新组件上的值复制到 tmp 组件类中,以便它们可以在生成的组件中使用。
它会像这样使用:
<div [compileBoundHtml]="someContentThatHasComponentHtmlInIt" [inputs]="anInput: anInputValue"></div>
希望这可以节省我不得不做的大量谷歌搜索。
【讨论】:
【参考方案5】:创建组件时可以轻松绑定:
createSub()
const factory = this.resolver.resolveComponentFactory(SubComponent);
const ref = this.location.createComponent(factory, this.location.length, this.location.parentInjector, []);
ref.someData = data: '123' ; // send data to input
ref.onClick.subscribe( // subscribe to event emitter
(event: any) =>
console.log('click');
)
ref.changeDetectorRef.detectChanges();
return ref;
发送数据非常简单,只需 ref.someData = data
其中data
是您要发送的数据。
从输出中获取数据也很容易,因为它是一个EventEmitter
,您可以简单地订阅它,并且只要您emit()
来自组件的值,您传入的clojure 就会执行。
【讨论】:
这实际上是使用输入名称设置数据吗?还是只使用字段名称? @bvdb 老实说我不确定,那是很久以前的事了,但您可以轻松检查。如果我猜我会说它正在使用字段名称,但不能确定。 我想它必须是ref.instance.someData
而不是 ref.someData
嘿@Alexander,这样做给了我 'someData' 不存在于类型 'unknown' 上。如果还没有解析动态组件的类型,如何修复知道类型?以上是关于处理 Angular 2 中动态创建的组件的 @Input 和 @Output的主要内容,如果未能解决你的问题,请参考以下文章