Search code examples
angulartypescriptdynamicangular2-components

How to use ResolveComponentFactory() but with a string as a key


What I'm trying to do:

  • Use something similar to the "resolveComponentFactory()", but with a 'string' identifier to get Component Factories.
  • Once obtained, start leverage the "createComponent(Factory)" method.

Plnkr Example -> enter link description here

In the example, you will see the AddItem method

addItem(componentName:string):void{
    let compFactory: ComponentFactory;

    switch(componentName){
        case "image":
            compFactory = this.compFactoryResolver.resolveComponentFactory(PictureBoxWidget);
            break;
        case "text":
            compFactory = this.compFactoryResolver.resolveComponentFactory(TextBoxWidget);
            break;
    }
    
    //How can I resolve a component based on string
    //so I don't need to hard cost list of possible options
    
    this.container.createComponent(compFactory);
}

The "compFactoryResolver:ComponentFactoryResolver" is injected in the contructor.

As you will note, having to hard code every permutation in a switch statement is less than ideal.

when logging the ComponentFactoryResolver to the console, I've observed that it contains a Map with the various factories.

CodegenComponentFactoryResolver {_parent: AppModuleInjector, _factories: Map}

However, this map is a private and can't be easily accessed (from what I can tell).

Is there a better solution then somehow rolling my own class to get access to this factory list?

I've seen a lot of messages about people trying to create dynamic components. However, these are often about creating components at run time. the components in question here are already pre-defined, I am stuck trying to access factories using a string as a key.

Any suggestions or advice are much appreciated.

Thank you kindly.


Solution

  • It's either defining a map of available components,

    const compMap = {
      text: PictureBoxWidget,
      image: TextBoxWidget
    };
    

    Or defining identifiers as static class property that will be used to generate a map,

    const compMap = [PictureBoxWidget, TextBoxWidget]
    .map(widget => [widget.id, widget])
    .reduce((widgets, [id, widget]) => Object.assign(widgets, { [id]: widget }), {});
    

    The map is then used like

    let compFactory: ComponentFactory;
    
    if (componentName in compMap) {
        compFactory = this.compFactoryResolver.resolveComponentFactory(compMap[componentName]);
    } else {
        throw new Error(`Unknown ${componentName} component`);
    }
    

    There's no way how component classes can be magically identified as strings, because they aren't resolved to strings in Angular 2 DI (something that was changed since Angular 1, where all DI units were annotated as strings).