2016-11-02 12 views
23

Jak obsługiwać/dostarczać właściwości @Input i @Output dla dynamicznie tworzonych komponentów w Angular 2?Uchwyt @ Input i @Output dla dynamicznie tworzonego komponentu w Angular 2

Ideą jest stworzenie dynamicznie (w tym przypadku) z podkomponent gdy createSub wywoływana jest metoda. Widoki są w porządku, ale jak mogę podać dane dla właściwości @Input w SubComponent. Ponadto, jak obsługiwać/subskrybować zdarzenia, które zapewnia SubComponent?

przykład: (Oba elementy są w tym samym NgModule)

AppComponent

@Component({ 
    selector: 'app-root' 
}) 
export class AppComponent { 

    someData: 'asdfasf' 

    constructor(private resolver: ComponentFactoryResolver, private location: ViewContainerRef) { } 

    createSub() { 
    const factory = this.resolver.resolveComponentFactory(SubComponent); 
    const ref = this.location.createComponent(factory, this.location.length, this.location.parentInjector, []); 
    ref.changeDetectorRef.detectChanges(); 
    return ref; 
    } 

    onClick() { 
    // do something 
    } 
} 

podrzędny

@Component({ 
    selector: 'app-sub' 
}) 
export class SubComponent { 
    @Input('data') someData: string; 
    @Output('onClick') onClick = new EventEmitter(); 
} 
+3

W przeszłości można było zrobić coś takiego ... 'ref.instance.someData = someData'. Nie jestem pewien, czy tak jest nadal. –

+2

To nadal działa. Nie ma innego sposobu (z wyjątkiem korzystania z usług wspólnych do komunikacji). http://stackoverflow.com/questions/36325212/angular-2-dynamic-tabs-with-user-click-chosen-components/36325468#36325468 zawiera więcej szczegółów. –

+0

@KrisHollenbeck @ Günter Zöchbauer Dzięki. Więc za każdym razem, gdy dane zmieniają się w komponencie nadrzędnym, muszę ustawić go jako "ref.instance.someData = someData" i uruchomić detekcję zmian ("ref.changeDetectorRef.detectChanges();")? A jak sprawdzić, która właściwość w podskładniku jest właściwa? Mógłby być nazwany zupełnie innym ...:/Może użyć 'Reflection' bezpośrednio Spróbuję :) – thpnk

Odpowiedz

0
createSub() { 
    const factory = this.resolver.resolveComponentFactory(SubComponent); 
    const ref = this.location.createComponent(factory, this.location.length, 
    ref.instance.model = {Which you like to send} 
    ref.instance.outPut = (data) =>{ //will get called from from SubComponent} 
    this.location.parentInjector, []); 
    ref.changeDetectorRef.detectChanges(); 
return ref; 
} 

SubComponent{ 
public model; 
public outPut = <any>{}; 
constructor(){ console.log("Your input will be seen here",this.model) } 
sendDataOnClick(){ 
    this.outPut(inputData) 
}  
} 
-1

Jeśli znasz typ komponentu, który chcesz dodać, myślę, że możesz zastosować inne podejście.

w aplikacji komponentu korzeń html:

<div *ngIf="functionHasCalled"> 
    <app-sub [data]="dataInput" (onClick)="onSubComponentClick()"></app-sub> 
</div> 

w aplikacji komponentu korzeń maszynopisu:

private functionHasCalled:boolean = false; 
private dataInput:string; 

onClick(){ 
    //And you can initialize the input property also if you need 
    this.dataInput = 'asfsdfasdf'; 
    this.functionHasCalled = true; 
} 

onSubComponentClick(){ 

} 
-1

dostarczanie danych dla @Input jest bardzo proste. Nazwałeś swoją app-sub i masz właściwość @ Input o nazwie data. Podanie tych danych może odbywać się w ten sposób:

<app-sub [data]="whateverdatayouwant"></app-sub> 
1

znalazłem następujący kod do generowania składników na bieżąco z ciągiem (angular2 generate component from just a string) i stworzył dyrektywę compileBoundHtml od niego, który przebiega wzdłuż danych wejściowych (nie obsługiwać wyjścia, ale myślę, że sama strategia miałaby zastosowanie więc można zmodyfikować to):

@Directive({selector: '[compileBoundHtml]', exportAs: 'compileBoundHtmlDirective'}) 
export class CompileBoundHtmlDirective { 
    // input must be same as selector so it can be named as property on the DOM element it's on 
    @Input() compileBoundHtml: string; 
    @Input() inputs?: {[x: string]: any}; 
    // keep reference to temp component (created below) so it can be garbage collected 
    protected cmpRef: ComponentRef<any>; 

    constructor(private vc: ViewContainerRef, 
       private compiler: Compiler, 
       private injector: Injector, 
       private m: NgModuleRef<any>) { 
     this.cmpRef = undefined; 
    } 
    /** 
    * Compile new temporary component using input string as template, 
    * and then insert adjacently into directive's viewContainerRef 
    */ 
    ngOnChanges() { 
     class TmpClass { 
      [x: string]: any; 
     } 
     // create component and module temps 
     const tmpCmp = Component({template: this.compileBoundHtml})(TmpClass); 

     // note: switch to using annotations here so coverage sees this function 
     @NgModule({imports: [/*your modules that have directives/components on them need to be passed here, potential for circular references unfortunately*/], declarations: [tmpCmp]}) 
     class TmpModule {}; 

     this.compiler.compileModuleAndAllComponentsAsync(TmpModule) 
      .then((factories) => { 
      // create and insert component (from the only compiled component factory) into the container view 
      const f = factories.componentFactories[0]; 
      this.cmpRef = f.create(this.injector, [], null, this.m); 
      Object.assign(this.cmpRef.instance, this.inputs); 
      this.vc.insert(this.cmpRef.hostView); 
      }); 
    } 
    /** 
    * Destroy temporary component when directive is destroyed 
    */ 
    ngOnDestroy() { 
     if (this.cmpRef) { 
     this.cmpRef.destroy(); 
     } 
    } 
} 

ważną modyfikacją jest dodanie:

Object.assign(this.cmpRef.instance, this.inputs); 

Zasadniczo kopiuje wartości, które mają być na nowym komponencie, do klasy komponentu tmp, aby mogły być używane w wygenerowanych komponentach.

Byłoby być używany jak:

<div [compileBoundHtml]="someContentThatHasComponentHtmlInIt" [inputs]="{anInput: anInputValue}"></div> 

Mam nadzieję, że w ten sposób zaoszczędzić komuś ogromnej ilości Googling musiałem zrobić.

2

można łatwo powiązać ją podczas tworzenia komponentu:

createSub() { 
    const factory = this.resolver.resolveComponentFactory(SubComponent); 
    const ref = this.location.createComponent(factory, this.location.length, this.location.parentInjector, []); 
    ref.someData = { data: '123' }; // send data to input 
    ref.onClick.subscribe(// subscribe to event emitter 
     (event: any) => { 
     console.log('click'); 
     } 
    ) 
    ref.changeDetectorRef.detectChanges(); 
    return ref; 
    } 

Wysyłanie danych jest naprawdę uczciwego, po prostu zrób ref.someData = data gdzie data jest dane chcesz wysłać.

Uzyskanie danych z wyjścia jest również bardzo łatwe, ponieważ jest to EventEmitter można po prostu zapisać się do niego, a zaklęcie, które zostanie przekazane, zostanie wykonane za każdym razem, gdy uzyskasz emit() wartość od komponentu.

Powiązane problemy