Ng2 - динамическое создание компонента на основе шаблона

Я рассматривал API Angular 2 для ComponentResolver и DynamicComponentResolver для создания динамических компонентов, но у меня есть что-то другое в виду, чем предлагаемые API.

Есть ли способ в NG2 создать компонент на основе строки имени класса?

Например, Im создает настраиваемую панель диаграмм. Каждый пользовательский макет хранится в базе данных, заявляя, что он хочет получить 2-х линейные диаграммы, 3х столбчатые диаграммы и т.д.

Когда я загружаю эти данные в json, это выглядит примерно так:

user.charts = [
     { type: 'LineChartComponent', position: ... }
     { type: 'BarChartComponent', position: ... }
];

Где type - это имя класса компонента, которое я хочу отразить.

До сих пор у меня есть следующее:

 this.chartMap = {
    'LineChartComponent': LineChartComponent
 };

let config = this.configuration;
let chartComponentType = this.chartMap[config.type];
let factory = this.componentFactory.resolveComponentFactory(chartComponentType);
let component = factory.create(this.injector);
component.instance.configuration = config;
this.chartContainer.insert(component.hostView);

Но вся идея состоит в том, чтобы устранить необходимость в chartMap. Как я могу отразить создание этого класса на основе строки без ссылки на этот тип?

Ответ 1

Update2:

Так как @estus, упомянутый в версии комментариев с классомName, не будет работать с минимизацией. Чтобы сделать это, работая с минимализацией, вы можете

1) добавьте статический ключ на каждый из ваших entryComponents, например:

export LineChartComponent {
  static key = "LineChartComponent";
}

а затем используйте этот key как уникальный.

const factoryClass = <Type<any>>factories.find((x: any) => x.key === compClassKey);

2) создайте словарь, например

export const entryComponentsMap = {
  'comp1': Component1,
  'comp2': Component2,
  'comp3': Component3
};

а затем

const factory = this.resolver.resolveComponentFactory(entryComponentsMap.comp1);

Update1:

Здесь версия из имени класса компонента

const factories = Array.from(this.resolver['_factories'].keys());
const factoryClass = <Type<any>>factories.find((x: any) => x.name === compClassName);
const factory = this.resolver.resolveComponentFactory(factoryClass);

Старая версия

Вы можете получить factory с помощью селектора компонентов, но вы должны использовать частную собственность.

Это может выглядеть примерно так:

const factories = Array.from(this.resolver['_factories'].values());
const factory = factories.find((x: any) => x.selector === selector);

Пример плунжера

Ответ 2

Можно также выполнить итерацию через import:

import * as possibleComponents from './someComponentLocation'
...
let inputComponent;
for(var key in possibleComponents ){
      if(key == componentStringName){
          inputComponent = possibleComponents[key];
          break;
      }
 }

Обновление: или просто: -)

let inputComponent = possibleComponents[componentStringName]

то вы можете создать экземпляр компонента, например:

if (inputComponent) {
    let inputs = {model: model};
    let inputProviders = Object.keys(inputs).map((inputName) => { return { provide: inputName, useValue: inputs[inputName] }; });
    let resolvedInputs = ReflectiveInjector.resolve(inputProviders);
    let injector: ReflectiveInjector = ReflectiveInjector.fromResolvedProviders(resolvedInputs, this.dynamicInsert.parentInjector);
    let factory = this.resolver.resolveComponentFactory(inputComponent as any);
    let component = factory.create(injector);
    this.dynamicInsert.insert(component.hostView);
}

Обратите внимание, что компонент должен находиться в @NgModule entryComponents