2

I have been using code like this to dynamically create components in my application. These components must support dynamic inputs. But now I tried updating to Angular 5 and ReflectiveInjector is deprecated. I would be very grateful if anyone would know how to help me to reuse this with Angular 5...

import {Component, Input, ViewContainerRef, ViewChild, ReflectiveInjector, ComponentFactoryResolver} from '@angular/core';
import {
  AComponent,
  BComponent,
  CComponent
} from './';

@Component({
  selector: 'dynamic-component',
  entryComponents: [
    AComponent,
    BComponent,
    CComponent
  ],
  template: `
    <div #dynamicComponentContainer></div>
  `
})
export class DynamicComponent {

  currentComponent = null;

  @ViewChild('dynamicPriceItemComponentContainer', { read: ViewContainerRef }) dynamicComponentContainer: ViewContainerRef;

  @Input() set componentData( data: { component: any, inputs: any }) {
    if (!data) {
      return;
    }

    let inputProviders = Object.keys(data.inputs).map((inputName) => {
      return { provide: inputName, useValue: data.inputs[inputName] };
    });

    let resolvedInputs = ReflectiveInjector.resolve(inputProviders);

    let injector = ReflectiveInjector.fromResolvedProviders(resolvedInputs, this.dynamicPriceItemComponentContainer.parentInjector);

    let factory = this.resolver.resolveComponentFactory(data.component);

    let component = factory.create(injector);

    this.dynamicComponentContainer.insert(component.hostView);

    if (this.currentComponent) {
      this.currentComponent.destroy();
    }

    this.currentComponent = component;
  }

  constructor(private resolver: ComponentFactoryResolver) {

  }
}

3 Answers 3

1

May this give some clue.

constructor(private renderer: Renderer2,
            private viewContainerRef: ViewContainerRef,
            private componentFactoryResolver: ComponentFactoryResolver) {

}

private createdDynamicComponent(): void {
    const factory = this.componentFactoryResolver.resolveComponentFactory(MyComponent);
    const createdComponentComponent = this.viewContainerRef.createComponent(factory);
    this.renderer.appendChild('element where you want append', createdComponentComponent.location.nativeElement);
}
Sign up to request clarification or add additional context in comments.

Comments

1

You need Injector.create Instead on ReflectiveInjector

@Input()
set dynamicContent(dc: { component: any; attribute: any; payload: any }) {
  if (!dc) {
    return;
  }
  const injector = Injector.create({
    providers: [
      {
        provide: dc.attribute,
        useValue: dc.payload
      }
    ],
    parent: this.dynamicComponentContainer.parentInjector
  });
  const factory = this.resolver.resolveComponentFactory(dc.component);
  const component = factory.create(injector);
  this.dynamicComponentContainer.insert(component.hostView);
}

You will of course need to solve the Reflection deprecation when injecting your input name in your actual dynamic children components. The key is to avoid injecting strings:

constructor(@Inject(LabelComponentAttributes) public ca: LabelComponentAttributes) {
  super();
  this.setCurrentStyles();
  this.setCurrentClasses();
}

Group all your input in a class and inject that class. Do not group them in interface as they are available only at runtime in typescript.

cheers

Comments

0

It looks like it will be possible in the future with NgComponentOutlet directive. Take a look at this issue. In the mean time maybe you can use ng-dynamic-component?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.