4

This is for the 2.1.0 Final release versions of Angular.

I'm trying to dynamically instantiate a component who's type is passed from a config JSON file. The best thing I can find is ComponentFactoryResolver, however all usages that I have found save one seem to be passing actual Type objects to the resolver.

Is there a way to get the Type from a string? Because presently, passing the type as a string gives me the error:

No component factory found for MyComponent

Relevant code:

StructureBuilder component:

private renderComponent(config:any) {
    let configComponents = config.components;
    if(config.hasOwnProperty('components') && configComponents.length){
        for(let i = 0, len = configComponents.length; i < len; i++){
            this.componentResolver.resolveComponentFactory(configComponents[i]);
        }
    }
}

where config.components is an array of strings that use the Type as a value.

And in the module definition (Structure.module.ts)

@NgModule({
    declarations: [
        MyComponent,
        StructureBuilder_Cmp
    ],
    entryComponents: [
        MyComponent
    ]
    //Etc...
});

As far as can tell with the sparse docs, this is exactly how you're supposed to do it.

If I change the dynamic string passed in from configComponents[i] to the actual imported type reference for MyComponent, it works.

So basically, the question is: Is there a way to use resolveComponentFactory with strings to resolve a component, and if not, is there way to get a Type reference from a string value?

dudewad
  • 13,215
  • 6
  • 37
  • 46
  • Do you want to pass `'MyComponent'` as string into `resolveComponentFactory`? – yurzui Oct 24 '16 at 15:54
  • Yes, sorry that's what I mean. – dudewad Oct 24 '16 at 15:55
  • Check this answer http://stackoverflow.com/questions/40115072/how-to-load-component-dynamically-using-component-name-in-angular2 – yurzui Oct 24 '16 at 15:56
  • @yurzui the problem with that answer is that it uses private, internal properties of the resolver. Private things like that are subject to change in future versions, so that's really not a reliable solution. – dudewad Oct 24 '16 at 16:45
  • Yes, you're right. This way you have to declare something like `map = { "MyComponent": MyComponent }` and use it – yurzui Oct 24 '16 at 16:48
  • Yeah that's what I'm doing now, it's working but has overhead. I think that's just how it's going to be! Thanks. – dudewad Oct 24 '16 at 17:10
  • If you passed in the components rather than the strings then it would obviously work. I would change to the code to do that. Then I would work backwards changing the code as I go back and eventually you'll find a good place to associate a name with a component (e.g. some lookup).. – fergal_dd Jan 19 '18 at 08:10

1 Answers1

9

My solution for now is to do what I really wanted to avoid, which is maintaining a key/val registry.

let componentRegistry = {
    'SomeComp1': SomeComp1, 
    'SomeComp2': SomeComp2
}

And then call it with:

private renderComponent(config:any) {
    let configComponents = config.components;
    if(config.hasOwnProperty('components') && configComponents.length){
        for(let i = 0, len = configComponents.length; i < len; i++){
            let comp = configComponents[i];
            this.componentResolver.resolveComponentFactory(this.componentRegistry[comp]);
        }
    }
}
dudewad
  • 13,215
  • 6
  • 37
  • 46