38 votes

Gérer @Input et @Output pour un composant créé dynamiquement dans Angular 2

Comment gérer et de fournir l' @Input et @Output propriétés pour créer dynamiquement des Composants Angulaire 2?

L'idée est de créer dynamiquement (dans ce cas), le sous-Composant lors de la createSub méthode est appelée. Fourches très bien, mais comment puis-je fournir des données pour l' @Input propriétés dans le sous-Composant. Aussi, comment gérer/abonnez-vous à l' @Output des événements de la sous-Composant fournit?

Exemple: (Les deux composants sont dans le même 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
  }
}

Sous-composant

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

13voto

Vous pouvez facilement le lier lorsque vous créez le composant:

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

L'envoi de données est vraiment direct, il suffit de faire ref.someData = datadata sont les données que vous souhaitez envoyer.

Obtenir des données à partir de la sortie est également très facile, car il s'agit d'un EventEmitter vous pouvez simplement vous y abonner et la clôture que vous transmettez s'exécutera chaque fois que vous emit() une valeur du composant.

3voto

William Neely Points 291

J'ai trouvé le code suivant pour générer des composants à la volée à partir d'une chaîne (angular2 génère un composant à partir d'un string) et a créé une compileBoundHtml directive qui transmet les données d'entrée (ne gère pas les résultats, mais je pense que la même stratégie s'appliquerait ainsi, vous pourriez modifier cette):

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

La modification importante est l'ajout de:

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

Fondamentalement, il copie les valeurs que vous voulez être sur le nouveau composant dans le tmp de classe de composant, de sorte qu'ils peuvent être utilisés dans les composants générés.

Il peut être utilisé comme:

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

J'espère que cela sauve quelqu'un de la quantité massive de Googler que j'avais à faire.

0voto

ayyappa maddi Points 156
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)
 }    
}

-2voto

Nour Points 1546

Si vous connaissez le type de composant que vous souhaitez ajouter, je pense que vous pouvez utiliser une autre approche.

Dans le composant racine de votre application html:

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

Dans le script de composition du composant racine de votre application:

 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(){

}
 

-7voto

La fourniture de données pour @Input est très simple. Vous avez nommé votre composant app-sub et il a une propriété @Input nommée data. La fourniture de ces données peut être effectuée en procédant comme suit:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X