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.

HDJEMAI
  • 9,436
  • 46
  • 67
  • 93
Edward
  • 1,076
  • 1
  • 12
  • 24
  • 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. – Estus Flask Oct 15 '16 at 19:22

1 Answers1

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

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • Thank you for the response. I'll look to integrate this into the solution I'm currently working on. – Edward Oct 17 '16 at 11:58
  • @DeveloperWonk No, there's a need. See https://stackoverflow.com/a/39522406/3731501 – Estus Flask Oct 27 '17 at 20:07
  • @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 https://stackoverflow.com/help/mcve for it. – Estus Flask Sep 04 '19 at 14:20
  • @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. – mathpaquette Sep 08 '19 at 14:40