2016-09-24 14 views
8

Próbuję wczytać dynamicznie komponent w ostatecznej wersji 2.0.0.Dynamiczne ładowanie istniejących komponentów Angular 2 Final Release

Korzystanie RC5 byłem ładowanie za pomocą następującego kodu:

Tworzenie dyrektywę załadować formantów:

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; 
     } 
     } 
    } 

następnie ładowane tę dyrektywę w moją stronę tak:

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

Ale po aktualizacji z wersji rc5 do wersji 2.0.0, resolver już nie istnieje, został zastąpiony przez kompilator.

Znalazłem mnóstwo miejsc pokazujących, jak załadować go za pomocą różnych kodów, ale wszystkie te zbyt skomplikowane i nie mogłem sprawić, by działało.

Weź to na przykład: How can I use/create dynamic template to compile dynamic Component with Angular 2.0?

Wygląda bardziej specyficzne dla tego scenariusza, My One po prostu trzeba załadować składnik i ustawić @Input nazywany modelem.

Jedną rzeczą, gdy próbowałem, musiałem stworzyć dynamicznie moduł dla każdego komponentu, a następnie dodać do niego komponent. Ale wtedy miałem problemy z powiedzeniem, że komponent był ustawiony w więcej niż jednym module, spróbuj usunąć w pewnym miejscu nie działa.

Główną część kodu pokazanego, mam z tego linku: http://blog.lacolaco.net/post/dynamic-component-creation-in-angular-2-rc-5/

i zrobił kilka zmian.

Aktualizacja

udaje mi się zrobić to praca, stosując następujące metody:

Sposób tworzenia została zmieniona na

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; 
     }); 
    } 

Większość miejsc znalazłem, set utwórz komponent i ustaw go na DynamicModule, problem z tym, że już deklarujesz ten sam komponent w innym module, kątowy będzie komp leżeć wokoło. Rozwiązaniem w moim przypadku było zaimportowanie mojego modułu ControlsModule, który eksportuje wszystkie moje formanty.

+0

Próbuję stworzyć coś podobnego. Mam plik JSON, który ładuje klasy i ich listę wykładów. Klasy są dodawane do paska nawigacji, a wykłady są dodawane jako lista w widoku szczegółowym. Po kliknięciu wykładu komponent powinien zostać załadowany w widoku szczegółów. Dla każdego wykładu istnieje inny komponent i zastanawiałem się nad utworzeniem modułu lub łącza routera dla każdego wykładu (i gniazda routera), zamiast po prostu ładować komponent, jak na przykład. Wciąż próbuję znaleźć najlepszy sposób ładowania rzeczy. – Atieh

Odpowiedz

10

Aktualizacja

NgComponentOutlet została wprowadzona w 4.0.0-beta.3https://github.com/angular/angular/commit/8578682

Wkrótce NgComponentOutlet

widzę dwie opcje, aby to zrobić:

1) Korzystanie z ComponentFactoryResolver.

Używa już wygenerowany fabryki i kod wygląda mniej więcej tak:

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; 
} 

W tym przypadku mamy do zdefiniowania dynamicznego komponentu w declarations i entryComponents właściwości w ciągu dekoratora modułu

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

2) Korzystanie Compiler

W tym przypadku możemy uruchomić tylko moduł kompilację przy użyciu compiler.compileModuleAndAllComponentsAsync a następnie wybrać komponent z componentFactories tablicy. Twój dyrektywa może wyglądać następująco:

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 to globalny serwis, który będzie ładować i przechowywać fabryki komponentów.

@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

Nadzieję, że to pomaga!

+1

dzięki za niesamowite wyjaśnienie, link podany przez rango.io jest dokładnie tym, co muszę zrobić, ponieważ mój komponent jest modalem, który wyrenderuje niektóre kontrolki w środku. Wszystkie dzięki za przykłady plunker. Bardzo mi pomogłeś. Czy są jakieś zalety przy użyciu ComponentFactoryResolver over Compiler? Używam pierwszego łatwiejszego. – Abner

+1

Nie ma za co! – yurzui

+1

Hej Yurzui ma przewagę jeden nad drugim? Widziałem, że drugi ma buforowanie, nie potrzebuję go. Próbowałem znaleźć sposób na zniszczenie komponentów nie tylko wtedy, gdy je odtwarzam, ale po zamknięciu dynamicznego modalu. – Abner