19

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.

1
  • 1
    How can you expect the class to be identified by a string, if it isn't identified by a string internally? The name of PictureBoxWidget class is something like a in minified app, and the name of TextBoxWidget class is likely a, too. You can maintain your own map of components, but you need to enumerate them explicitly. It is always more solid approach to have a fixed list of alternatives that can be validated and tested. Commented Oct 15, 2016 at 19:22

1 Answer 1

22

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

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for the response. I'll look to integrate this into the solution I'm currently working on.
@DeveloperWonk No, there's a need. See stackoverflow.com/a/39522406/3731501
@MathieuPaquette Not normal, no. I'd expect the problem to be specific to your services. Consider asking a new question that reflects your case and providing stackoverflow.com/help/mcve for it.
@EstusFlask sorry, I removed my question after realising that some interfaces were declared and used by the components and compMap creating this circular dep. ;) thanks.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.