Error While Reusing Component That Have Been Loade

2019-08-27 17:55发布

问题:

I Have created a component that load on the fly a child component with a template which are comming from my server like this topic

Compile dynamic HTML in Angular 4/5- something similar to $compile in Angular JS

I Would like to call this component from anywhere in my application, Its selector may be called from a modal template (I cannot reload the modal multiple time), It may be called from the browser (And I cannot load both modal and browser in the same time)

@Component({
  selector: 'my-component',
  template: `<h2>Stuff bellow will get dynamically created and injected<h2>
          <div #vc></div>`
})
export class TaggedDescComponent {
  @ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef;

  private cmpRef: ComponentRef<any>;

  constructor(private compiler: Compiler,
              private injector: Injector,
              private moduleRef: NgModuleRef<any>,
              private backendService: backendService,
              ) {}

  ngAfterViewInit() {
    // Here, get your HTML from backend.
    this.backendService.getHTMLFromServer()
        .subscribe(rawHTML => this.createComponentFromRaw(rawHTML));
  }

  // Here we create the component.
  private createComponentFromRaw(template: string) {
    // Let's say your template looks like `<h2><some-component [data]="data"></some-component>`
    // As you see, it has an (existing) angular component `some-component` and it injects it [data]

    // Now we create a new component. It has that template, and we can even give it data.
    const tmpCmp = Component({ template, styles })(class {
      // the class is anonymous. But it's a quite regular angular class. You could add @Inputs,
      // @Outputs, inject stuff etc.
      data: { some: 'data'};
      ngOnInit() { /* do stuff here in the dynamic component */}
    });

    // Now, also create a dynamic module.
    const tmpModule = NgModule({
      imports: [RouterModule],
      declarations: [tmpCmp],
      // providers: [] - e.g. if your dynamic component needs any service, provide it here.
    })(class {});

    // Now compile this module and component, and inject it into that #vc in your current component template.
    this.compiler.compileModuleAndAllComponentsAsync(tmpModule)
      .then((factories) => {
        const f = factories.componentFactories[0];
        this.cmpRef = f.create(this.injector, [], null, this.moduleRef);
        this.cmpRef.instance.name = 'my-dynamic-component';
        this.vc.insert(this.cmpRef.hostView);
      });
  }

  // Cleanup properly. You can add more cleanup-related stuff here.
  ngOnDestroy() {
    if(this.cmpRef) {
      this.cmpRef.destroy();
    }
  }
}

But when this part are reloadded (without refreshing the browser) I Have this error :

ERROR Error: Type e is part of the declarations of 2 modules: function(){} and function(){}! Please consider moving e to a higher module that imports function(){} and function(){}. You can also create a new NgModule that exports and includes e then import that NgModule in function(){} and function(){}.
    at le (main.js:1)
    at e._addTypeToModule (main.js:1)
    at main.js:1
    at Array.forEach (<anonymous>)
    at e.getNgModuleMetadata (main.js:1)
    at e._loadModules (main.js:1)
    at e._compileModuleAndAllComponents (main.js:1)
    at e.compileModuleAndAllComponentsAsync (main.js:1)
    at e.compileModuleAndAllComponentsAsync (main.js:1)
    at e.createComponentFromRaw (main.js:1)

回答1:

Since the component which you wants to render dynamically needs to be imported in the component and needs to be registered in the same components parent module.

If you want to use the same component from some other place of application it causes few issues, which have few workarounds on them. You can follow below article to learn more on same, https://blog.angularindepth.com/here-is-what-you-need-to-know-about-dynamic-components-in-angular-ac1e96167f9e

But the process becomes quite complicated, I will highly recommend you using portals which can be newly added to angular CDK, A Portal is a piece of UI that can be dynamically rendered to an open slot on the page. The “piece of UI” can be either a Component or a TemplateRef and the "open slot" is a PortalHost.

There is a nice presentational video on same.

https://www.youtube.com/watch?v=YQMIR01dQew

Hope this helps, All the Best!!