How do you use @Input with components created with a ComponentFactoryResolver?

Is there a method that can be used to define an @Input property on an Angular 2 component that's created dynamically?

I'm using the ComponentFactoryResolver to create components in a container component. For example:

let componentFactory = this.componentFactoryResolver.resolveComponentFactory(componentName);


let componentRef = entryPoint.createComponent(componentFactory);

Where "entryPoint" is something like this in the component HTML:

<div #entryPoint></div>

And defined in my container component with:

@ViewChild('entryPoint', { read: ViewContainerRef } entryPoint: ViewContainerRef;

This works well, but I can't find a way to make an @Input property work on the newly created component. I know that you can explicitly set public properties on the component class, but this doesn't seem to work with ng-reflect. Prior to making this change I had a "selected" property decorated with "@Input()" that caused Angular to add the following to the DOM:

<my-component ng-reflected-selected="true"></my-component>

With this in place I was able to dynamically update the markup to switch a CSS class:

<div class="header" [class.active-header]="selected === true"></div>

Based on some searching I was able to find a method to make "@Output" work as expected, but I've yet to find anything for @Input.

Let me know if additional context would be helpful and I'd be happy to add it.

30914 次浏览

No, Angular2 bindings only work with components and directives statically added to the template of a component.

For all other situations use shared services like explained in https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service

You can also use

let componentRef = entryPoint.createComponent(componentFactory);
componentRef.instance.someProp = 'someValue';
componentRef.instance.someObservableOrEventEmitter.subscribe(data => this.prop = data);

The Günter Zöchbauer's answer is correct. But if you, like I was, have troubles with rendering @Input data in the component template, you should try this:

  1. Run your component creating code in ngAfterViewInit.
  2. Add componentRef.instance.ngOnInit() as a last line.
  3. Inside ngOnInit of your dynamically created component run detectChanges method of the ChangeDetectorRef.

Since 14.1.0, Angular added the setInput() method to directly set or update @Input to the component reference.

this.componentRef.setInput('someInput', this.value);

Using this method will properly mark for check component using the OnPush change detection strategy. It will also assure that the OnChanges lifecycle hook runs when a dynamically created component is change-detected.

If the value will change, I use this approach:

@Input()
public value: string;


ngOnInit(): void {
this.viewContainerRef.clear();
this.componentRef = this.viewContainerRef.createComponent(this.component);
}


ngOnChanges(changes: SimpleChanges): void {
if (changes.value) {
this.componentRef?.setInput('someInput', this.value); // Updated with every change from the parent component.
}
}


ngOnDestroy(): void {
this.componentRef?.destroy(); // Don't forget to destroy the component.
}

API