3

To allow customer customization, I need a way to load component dynamically using a string, taken from backend.

I see this usefull guide: https://medium.com/angular-in-depth/loading-components-dynamically-in-angular-cd13b9fdb715

but, as other tutorial like the official one, they always assume you already know classnames.

Look at this code from the tutorial:

async loadComponent(vcr: ViewContainerRef, isLoggedIn: boolean) {
    const { GuestCardComponent } = await import('./guest-card/guest-card.component');
    const { UserCardComponent } = await import('./user-card/user-card.component');
    ...

The string inside import is not a problem but the definition of class is.

I need a placeholder in my template for loading a component I know I have in the FileSystem but I don't know its name.

Something like this:

From backend i get:

var dynamic_loading_component = { "classname" : "AdidasCardComponent" , "path" : "/components/adidas.component" }

then I would like to use like this:

const { dynamic_loading_component["classname"] } = await import(dynamic_loading_component["path"]);

vcr.clear();
let component : any = dynamic_loading_component["classname"];
   
return vcr.createComponent(this.cfr.resolveComponentFactory(component))

Some sort of Reflection.

Is it possible?

EviSvil
  • 510
  • 3
  • 21
  • 1
    Does this answer your question? [Angular 2: Load Component Based on service response](https://stackoverflow.com/questions/42465294/angular-2-load-component-based-on-service-response) – Akif Oct 05 '20 at 08:52
  • Im afraid not because in that example all your possible component are always pre-know as this rows: import {Widget1Component} from '../widget/widget1.component'; import {Widget2Component} from '../widget/widget2.component'; – EviSvil Oct 05 '20 at 10:01
  • Does this answer your question? [How to instantiate a Class from a String in JavaScript](https://stackoverflow.com/questions/49042459/how-to-instantiate-a-class-from-a-string-in-javascript) – Robert Dempsey Oct 05 '20 at 10:24
  • I already come to eval but when I try to eval this: const { ExampleComponent } = await import('../pages/example/example.component'); i got this exception: Uncaught (in promise): SyntaxError: await is only valid in async function – EviSvil Oct 05 '20 at 10:26

1 Answers1

3

I finally found a way merging some other posts. Its the best I could get.

In the page where you have to dynamic load component, have this:

@ViewChild(PlaceHolderDirective, { static: true })
placeholderHost: PlaceHolderDirective;

...

ngAfterViewInit() 
{
  const viewContainerRef  = this.placeholderHost.viewContainerRef;
  var component_data = { "classname" : "ExampleComponent" }; //this is an example
  this._componentLoader.loadComponent(viewContainerRef, {} );
}

PlaceHolderDirective is:

import { Directive, ViewContainerRef } from '@angular/core';

@Directive({ selector: '[component-placeholder]' })
export class PlaceHolderDirective {
  constructor(public viewContainerRef: ViewContainerRef) {}
}

Now, create a Service for loading component dynamically:

import { Injectable,ComponentFactoryResolver, ViewContainerRef, NgModuleFactoryLoader, Injector, Type } from '@angular/core';

@Injectable({ providedIn: 'root' })

export class ComponentLoaderService 
{
  constructor(
    private cfr                   : ComponentFactoryResolver) 
  {
    //constructor
  }

  async loadComponent(vcr: ViewContainerRef, component_data = {} ) 
  {
    vcr.clear();
    const factories     = this.cfr['ngModule']['instance']['__proto__']['constructor']['__annotations__'][0]['declarations'];
    var factoryClass    = <Type<any>>factories.find((x: any) => x.name === component_data["classname"] );
    const factory       = this.cfr.resolveComponentFactory(factoryClass);
    return vcr.createComponent(factory);
  }
}

You still have to register your component in app.module but its the only section you need to write.

EviSvil
  • 510
  • 3
  • 21
  • Indeed this [post](https://stackoverflow.com/questions/40115072/how-to-load-component-dynamically-using-component-name-in-angular2) use same solution – Camille May 19 '21 at 10:02