简体   繁体   中英

Angular: How to use shared modules in a Dynamic Component?

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.

在此处输入图片说明

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);
  }

}

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]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM