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

Edward picture Edward · Oct 15, 2016 · Viewed 13.7k times · Source

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.

Answer

Estus Flask picture Estus Flask · Oct 15, 2016

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).