webentwicklung-frage-antwort-db.com.de

Behandeln Sie @Input und @Output für dynamisch erstellte Komponenten in Angular 2

Wie werden @Input- und @Output-Eigenschaften für dynamisch erstellte Komponenten in Angular 2 behandelt/bereitgestellt?

Die Idee ist, die SubComponent dynamisch zu erstellen (in diesem Fall), wenn die Methode createSub aufgerufen wird. Gabeln gut, aber wie gebe ich Daten für die @Input-Eigenschaften in der SubComponent zur Verfügung. Wie werden die @Output-Ereignisse, die die SubComponent bereitstellt, behandelt/abonniert?

Beispiel: (Beide Komponenten befinden sich im selben 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
  }
}

SubComponent

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

Sie können es leicht binden, wenn Sie die Komponente erstellen:

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

Das Versenden von Daten ist wirklich geradlinig, tun Sie einfach ref.someData = data, wobei data die Daten ist, die Sie senden möchten.

Das Abrufen von Daten aus der Ausgabe ist auch sehr einfach, da es sich um eine EventEmitter handelt, die Sie einfach abonnieren können. Die von Ihnen übergebene Clojure-Funktion wird immer ausgeführt, wenn Sie emit() einen Wert aus der Komponente eingeben. 

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

Ich habe den folgenden Code gefunden, um Komponenten schnell aus einem String zu generieren ( angle2 generiert eine Komponente nur aus einem String ) und erstellte daraus eine compileBoundHtml-Direktive, die Eingabedaten weitergibt (nicht mit Ausgaben, aber ich denke das Gleiche.) Strategie würde gelten, so dass Sie dies ändern könnten):

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

Die wichtige Änderung besteht in der Hinzufügung von:

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

Grundsätzlich kopiert es die Werte, die Sie in der neuen Komponente haben möchten, in die tmp-Komponentenklasse, damit sie in den generierten Komponenten verwendet werden können.

Es würde verwendet werden wie:

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

Hoffentlich erspart dies jemandem das massive Googeln, das ich tun musste.

0
William Neely