2016-09-24 4 views
8

Я пытаюсь загрузить динамически компонент в финальной версии 2.0.0.Динамически загружать существующие компоненты Угловой 2 Окончательный выпуск

Использование RC5 я загружал, используя следующий код:

Создать директиву для загрузки элементов управления:

import { 
    CheckboxComponent, CheckboxListComponent,DatePickerComponent 
} from '../components/'; 

@Directive({ 
     selector: '[ctrl-factory]' 
    }) 
    export class ControlFactoryDirective implements OnChanges { 
     @Input() model: any; 

     constructor(private vcRef: ViewContainerRef, private resolver: ComponentResolver) { 
     } 

     create(cp) { 
     this.resolver.resolveComponent(cp) 
      .then(factory => { 
      const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector); 
      this.vcRef.createComponent(factory, 0, injector, []); 
      let ch = this.vcRef.createComponent(factory, 0, injector, []).instance; 
      ch.model = this.model; 
     }); 
     } 

     ngOnChanges() { 
     if (!this.model) return; 

     switch (this.model.type) { 
      case 'checkbox': 
      this.create(CheckboxComponent); 
      break; 
      case 'checkboxlist': 
      this.create(CheckboxListComponent); 
      break; 
      case 'datepicker': 
      this.create(DatePickerComponent); 
      break; 
      default: 
      break; 
     } 
     } 
    } 

Затем загружается эту директиву в моей страницы, как это:

<div ctrl-factory *ngFor="let child of page.childrens" [model]="child"></div> 

Но после обновления с окончательной версии от rc5 до 2.0.0, resolver больше не существует, был заменен компилятором.

Я нашел множество мест, показывающих, как загрузить его с использованием разных кодов, но все это слишком сложно, и я не мог заставить его работать.

Возьмите это, например: How can I use/create dynamic template to compile dynamic Component with Angular 2.0?

Он выглядит более конкретно к этому сценарию, мой один мне просто нужно загрузить компонент и установить @Input называется моделью.

Одна вещь, когда я пытался создать динамически модуль для каждого компонента, а затем добавить к нему компонент. Но потом у меня возникли проблемы с тем, что компонент был установлен в более чем одном модуле, попытайтесь удалить в некотором месте нерабочее.

Основная часть кода, показанного, я получаю от этой ссылке: http://blog.lacolaco.net/post/dynamic-component-creation-in-angular-2-rc-5/

И сделал несколько изменений.

Update

мне удалось заставить его работать, используя следующий подход:

Метод создания был изменен на

private create(cp) { 
    @NgModule({ 
     imports: [BrowserModule, ControlsModule], 
     declarations: [] 
    }) 
    class DynamicModule {} 

    this.compiler.compileModuleAndAllComponentsAsync(DynamicModule) 
     .then(({componentFactories}) => { 
     const compFactory = componentFactories.find(x => x.componentType === cp); 
     const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector); 
     const cmpRef = this.vcRef.createComponent(compFactory, 0, injector, []); 
     cmpRef.instance.model = this.model; 
     }); 
    } 

Большинство мест, которые я нашел, set создайте компонент и установите его в DynamicModule, проблема в том, что вы уже заявляете, что тот же компонент в другом модуле, угловой собирается comp Остерегайтесь. Решение в моем случае состояло в том, чтобы импортировать мой ControlsModule, который экспортирует все мои элементы управления.

+0

Я пытаюсь создать подобную вещь. У меня есть файл JSON, который загружает классы и их список лекций. Классы добавляются в панель навигации, а лекции добавляются в виде списка в подробном виде. При нажатии на лекцию компонент должен быть загружен в подробном представлении. Для каждой лекции есть другой компонент, и я думал о создании модуля или ссылки маршрутизатора для каждой лекции (и роутера-выхода) вместо того, чтобы просто загружать компонент, например, ваш пример. Я все еще пытаюсь найти лучший способ загрузить вещи. – Atieh

ответ

10

Update

NgComponentOutlet был введен в 4.0.0-beta.3https://github.com/angular/angular/commit/8578682

Скоро NgComponentOutlet

Я вижу два варианта, чтобы сделать это:

1) Использование ComponentFactoryResolver.

Он использует уже созданный завод и код выглядит примерно так:

constructor(private vcRef: ViewContainerRef, private resolver: ComponentFactoryResolver) { } 
create(comp) { 
    const factory = this.resolver.resolveComponentFactory(comp); 
    const compRef = this.vcRef.createComponent(factory); 

    (<any>compRef).instance.model = this.model; 
} 

В этом случае мы должны определить динамическую составляющую в declarations и entryComponents свойств в декоратора модуля

@NgModule({ 
    imports:  [ BrowserModule ], 
    declarations: [ AppComponent, DynamicComponent ], 
    entryComponents: [DynamicComponent], 
    bootstrap: [ AppComponent ] 
}) 
export class AppModule { } 

2) Использование Compiler

В этом случае мы можем только запустить модуль компиляции с помощью compiler.compileModuleAndAllComponentsAsync, а затем найти компонент из массива componentFactories. Ваша директива может выглядеть следующим образом:

constructor(private vcRef: ViewContainerRef, private loader: DynamicLoaderService) { } 

create(comp) { 
    this.loader.createComponentFactory(comp).then(factory => { 
    const compRef = this.vcRef.createComponent(factory); 

    (<any>compRef).instance.model = this.model; 
    }) 
} 

DynamicLoaderService глобальный сервис, который будет загружать и хранить компонентные фабрики.

@Injectable() 
export class DynamicLoaderService { 
    constructor(protected compiler: Compiler) {} 

    private resolveCompHelper$ = new Subject<any>(); 
    private cache = new Map<string, ComponentFactory<any> | number>(); 

    public createComponentFactory(type: string) : Promise<ComponentFactory<any>> { 
    let factory = this.cache.get(type); 

    // if factory has been already loading 
    if(factory === 1) { 
     return new Promise((resolve) => { 
     // waiting compilation of factory 
     const subscriber = this.resolveCompHelper$.subscribe((data) => { 
      if(type !== data.type) return; 
      subscriber.unsubscribe(); 
      resolve(data.factory); 
     }); 
     }); 
    } 
    // factory exists in cache 
    if (factory) { 
     return new Promise((resolve) => resolve(factory)); 
    } 

    const comp = typeMap[type]; 
    // factory startes loading 
    this.cache.set(type, 1); 
    return new Promise((resolve) => { 
     this.compiler.compileModuleAndAllComponentsAsync(createComponentModule(comp)) 
     .then((moduleWithFactories: ModuleWithComponentFactories<any>) => { 
      factory = moduleWithFactories.componentFactories 
       .find(x => x.componentType === comp); 
      this.cache.set(type, factory); 
      this.resolveCompHelper$.next({ type, factory}); 

      resolve(factory); 
     }); 
    }); 
    } 
} 

Plunker Example

Надеется, что это помогает!

+1

спасибо за удивительное объяснение, ссылка, которую вы предоставляете от rango.io, это то, что мне нужно сделать, потому что мой компонент является модальным, который будет отображать некоторые элементы управления внутри. Все спасибо за примеры plunker. Вы мне очень помогли. Есть ли преимущества использования ComponentFactoryResolver над компилятором? Я нахожу использование первого проще. – Abner

+1

Добро пожаловать! – yurzui

+1

Эй, Юрзуй какое-то преимущество одно над другим? Я видел, что второй имеет кеширование, мне это не нужно. Я пытался найти способ уничтожить компоненты не только тогда, когда я их воссоздал, но как только я закрыл динамический модальный. – Abner

0

Это было полезно для меня, чтобы увидеть этот вариант, основанный на директиве, но я нашел тот, который соответствует моим потребностям на основе компонентов: https://www.ag-grid.com/ag-grid-angular-aot-dynamic-components/

Счастливого кодирование!

Смежные вопросы