0

Im trying to add the component and module dynamically as shown below

Component:

protected createNewComponent (tmpl:string) {
  @Component({
      selector: 'dynamic-component',
      template: tmpl,
      providers: [{provide: CustomDynamicComponent, useExisting CustomDynamicComponent}]
  })
  class CustomDynamicComponent  implements IHaveDynamicData {
      @Input()  public entity: any;
  };
  // a component for this particular template
  return CustomDynamicComponent;
}

Here tmpl template is a dynamic string.

Module:

protected createComponentModule (componentType: any) {
  @NgModule({
    imports: [
    ],
    declarations: [
      componentType
    ],
  })
  class RuntimeComponentModule
  {
  }
  // a module for just this Type
  return RuntimeComponentModule;
}

Here componentType is the component I want to create.

Now, creating component and module dynamically using

let type   = this.createNewComponent(template);
let module = this.createComponentModule(type);

and compiling the markup using

this.compiler
        .compileModuleAndAllComponentsAsync(module)
        .then((moduleWithFactories) => {})

On compiling this code, Im observing typescript compilation errors as shown below.

enter image description here enter image description here

How to get rid of this compilation error in angular version14.

Thanks

1 Answer 1

2

I wouldn't do something that complicated :

@Injectable({ providedIn: 'root' })
class Foo {
  rand = Math.random();
}

@Component({
  selector: 'hello',
  template: '<div #container></div>',
})
export class HelloComponent implements AfterViewInit {
  @ViewChild('container', { read: ViewContainerRef })
  container: ViewContainerRef;

  constructor(
    private injector: Injector,
    private environement: EnvironmentInjector
  ) {}

  ngAfterViewInit() {
    this.environement.runInContext(() => { // important part to allow DI with inject()
      // Define the component using Component decorator.
      const component = Component({
        selector: 'test',
        template:
          '<div>This is the dynamic template. Test value: {{test}}</div>',
        styles: [':host {color: red}'],
        providers: [{ provide: Foo, useClass: Foo }],
      })(
        class {
          private foo = inject(Foo);

          constructor() {
            console.log(this.foo.rand);
          }

          test = 'some value';
        }
      );

      // Define the module using NgModule decorator.
      const module = NgModule({ declarations: [component] })(class {});

      const componentRef = this.container.createComponent(component, {
        injector: this.injector,
        ngModuleRef: createNgModuleRef(module, this.injector),
      });
      setTimeout(() => (componentRef.instance.test = 'some other value'), 2000);
    });
  }
}

Playground

Sign up to request clarification or add additional context in comments.

5 Comments

I would like to also include providers array here, to provide the useExisting component class as below. providers: [{ provide: MyCompClass, useExisting: MyComp }]. But this is not working. On adding providers, Im able to see the same compile errors.
why do you need provider that basically does nothing? you can still inject component instance by its class in its components subtree without that provider
@bvakiti useExisting is meant to map one token to another, that can't work like that. And what isn't working with my example, please give us more details.
@MatthieuRiegler I would like to use this component by other class too. So I want to add this component to providers array, may be using useClass
@bvakiti Since you create your class yourself, you can't use the traditionnal dependency injection. But since Angular 14.2, there is a solid alternative with inject() and EnvironmentInjector.runInContext(). Please see my update !

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.