0

I'm creating dynamic component in Angular v6 using compileModuleAndAllComponentsAsync of angular compiler with same below code.

viewChangeMethod(view: string) {
    let template = `<span>${view} </span>`;
    const tmpCmp = Component({ template: template })(class { });
    this._compiler.clearCacheFor(this.tmpModule)
    this.tmpModule = NgModule({ declarations: [tmpCmp,FRAComponent],import:[ComonModule] })(class {
    });

    this._compiler.compileModuleAndAllComponentsAsync(this.tmpModule)
        .then((factories) => {
            const f = factories.componentFactories[0];
            const cmpRef = f.create(this._injector, [], null, this._m);
            this._container.detach()
            console.log(cmpRef.hostView)
            this._container.insert(cmpRef.hostView);
        })
    this._compiler.clearCacheFor(this.tmpModule)
}

Every thing is working fine but when importing any shared module or custom module below error.

enter image description here

Narm
  • 10,677
  • 5
  • 41
  • 54
DigguDg
  • 19
  • 1
  • 10
  • I don't see where or how you're trying to import the modules and use them - can you include that code? Are you adding an `imports[]` and adding the required modules into this line `this.tmpModule = NgModule({ declarations: [tmpCmp] })(class {`? – Narm Jul 18 '18 at 14:32
  • Yes, I'm importing component and module in tmpModule – DigguDg Jul 18 '18 at 15:14

2 Answers2

2

If you will be requiring shared modules or any additional modules for you Dynamic Component I recommend you take a slightly different approach to creating you Dynamic Component.

My projects use dynamic components and I also import various modules into them, but I've used the following approach to creating my components - Full working StackBlitz

Above is a StackBlitz example I created if you want to adjust that to your needs || below is a copy of the code.

import {
  Component, ViewChild, AfterContentInit, ComponentFactoryResolver,
  Compiler, ViewContainerRef, NgModule, NgModuleRef
} from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

@Component({
  selector: 'app',
  template: '<ng-template #vc></ng-template>',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterContentInit {
  @ViewChild('vc', { read: ViewContainerRef }) _container: ViewContainerRef;
  private cmpRef;

  constructor(
    private componentFactoryResolver: ComponentFactoryResolver,
    private compiler: Compiler,
    private _m: NgModuleRef<any>) { }


  ngAfterContentInit() {
    this.addComponent();
  }

  public sayHello(): void{
    alert("Hello!");
  }

  private addComponent(): void {
    @Component({
      template: `<h2>This is a dynamic component</h2>
      <button (click)="_parent.sayHello()">Click me!</button>`
    })
    class DynamicComponent {
      constructor(public _parent: AppComponent) { }
    }
    @NgModule({
      imports: [
        BrowserModule
      ],
      declarations: [DynamicComponent],
    }) class DynamicModule { }

    const mod = this.compiler.compileModuleAndAllComponentsSync(DynamicModule);
    const factory = mod.componentFactories.find((comp) =>
      comp.componentType === DynamicComponent
    );
    this.cmpRef = this._container.createComponent(factory);
  }

}
Narm
  • 10,677
  • 5
  • 41
  • 54
  • Hi, thanks your code is working fine. But when importing any other component in dynamic component, then it's giving error. Please see my code https://stackblitz.com/edit/angular-yjsfpd?file=src%2Fapp%2FviewContainer.component.ts – DigguDg Jul 19 '18 at 08:30
  • Ah okay, now I understand what is going on. You're wanting to use a dynamic component to inject another (static) component into the DOM. I don't know any solution using Dynamic Components to achieve that. This sounds like a use case better suited for Angular Elements to me. https://angular.io/guide/elements – Narm Jul 19 '18 at 15:22
0

You can follow below solution to get your work done, I have verified it previous NG versions, you can try integrating in your solution.

Define a service like this

import { Injectable, Type, Component, ComponentFactoryResolver, ViewContainerRef, ComponentRef, QueryList, ComponentFactory } from '@angular/core';

@Injectable()
export class DynamicComponentService {
    constructor(private componentFactoryResolver: ComponentFactoryResolver) {
    }

    public append(componentType: Type<any>, where: ViewContainerRef): ComponentRef<any> {
        const componentFactory = this.resolveFactory(componentType);
        const componentRef = where.createComponent(componentFactory);
        return componentRef;
    }

    public resolve (componentType: Type<any>): ComponentFactory<any> {
        const componentFactory: ComponentFactory<any> = this.componentFactoryResolver.resolveComponentFactory(componentType);
        return componentFactory;
    }
}

Above service is responsible for injecting your component dynamically.

Also note that you don't need to pass your component as a string you would need to pass its type.

Read above class carefully before implementing it.

Usage –

Inject service in your component also in component take View Container Reference (in my code below this.viewContainerReference, this is the place where dynamic component has to be injected)

componentReference = this.dynamicComponentService.append(ComponentClass, this.viewContainerReference);
componentReference.instance.someProp = whatever;

someProp in above is the public property of your component, you can pass some data to it.

You can import your module as usual as part of @NgModule imports and components Also ensure you have schemas: [NO_ERRORS_SCHEMA]

TARJU
  • 1,785
  • 1
  • 14
  • 16