Wie lade ich eine untergeordnete Nativescript Angular-Komponente mit dem Code dahinter?

Oct 01 2020

Ich versuche, einem GridLayout, das in der Vorlage des übergeordneten Elements definiert ist, eine benutzerdefinierte untergeordnete Angular-Komponente hinzuzufügen. Das übergeordnete Element muss einige Komponenten unterstützen, die es erstellen und mit denen es interagieren kann, es muss jedoch in der Lage sein, sie zu platzieren, wenn und wo dies im GridLayout angewiesen ist.

  • Ich kann mein SampleComponent-Kind in der Vorlage angeben und es wird angezeigt.
  • Wenn ich versuche, meine SampleComponent mithilfe von Code dahinter zum GridLayout hinzuzufügen, gibt die Logik grid.addChildChild keine Fehler aus, die Komponente wird jedoch nicht angezeigt.
  • Wenn ich dem Raster mithilfe des dahinter liegenden Codes eine Schaltfläche hinzufüge, wird diese wie erwartet angezeigt.

Ich verstehe, dass ich versuche, eine Komponente gegen eine Schaltfläche zu laden, aber meine Komponente erweitert ContentView. Ich habe einige Diskussionen über Builder gesehen, aber sie schienen aus dem Quellcode zu kompilieren, in dem meine untergeordneten Komponenten mit meinem übergeordneten Element erstellt wurden, und sie verwenden XML-Vorlagen. Ich habe versucht, mir ComponentBuilder anzusehen, aber ich kann keine Dokumentation finden, die mir hilft, zu verstehen, wie ich es verwenden könnte.

Die wichtigsten 3 Funktionen meines Beispiels sind unten aufgeführt. Sie werden vom Benutzer initiiert, der "Beispiel" oder "Schaltfläche" eingibt und auf HINZUFÜGEN klickt, um onTap () auszulösen:

    onTap() {
        this.textField.nativeElement.dismissSoftInput();
        let name = this.textField.nativeElement.text.toLowerCase();
        var component: any;
        console.log(`Adding component ${name}`); switch( name ) { case 'sample': component = this.buildSample(); break; case 'button': component = this.buildButton(); break; default: console.error("User keyed in invalid response"); return; } console.log("Adding component to grid"); let grid: GridLayout = this.gridField.nativeElement; grid.addRow( new ItemSpec( 1, GridUnitType.AUTO )); let label = new Label(); label.text = name; grid.addChild( label ); GridLayout.setRow( label, this.row ); grid.addChild( component ); GridLayout.setRow( component, this.row ); GridLayout.setColumn( component, 1 ); this.row++ } private buildButton(): Button { let button = new Button(); button.text = `Button for row${this.row}`;
        return button;
    }

    private buildSample(): SampleComponent {
        let sample = new SampleComponent();
        sample.setting = 259;
        return sample;
    }

Antworten

JeffWheatley Oct 15 2020 at 19:50

Die Schlüssel, um diese Arbeit mit einem NativeScript Angular-Paradigma zu ermöglichen, scheinen Direktiven und Injektionen zu sein.

Meine Lösung besteht darin, eine Direktive zum Laden meiner untergeordneten Direktive zu verwenden. Die Direktive verwendet Injection, um die Referenzen abzurufen, die zum Erstellen der untergeordneten Komponente in der Ansicht des übergeordneten Elements erforderlich sind.

Also, in meinem Beispiel unten,

<StackLayout *LoadChildComponent></StackLayout>

Feuert mein LoadChildDirective ab, umschließt aber auch das Stack-Element

<ng-template loadChild><StackLayout></StackLayout></ng-template>

Dies bedeutet, dass der Ansichtskontext der Direktive die Position direkt außerhalb des Stapelelements ist.

  • Wie Komponenten kann die Direktive Injection verwenden, um coole Inhalte abzurufen, mit denen wir eine untergeordnete Komponente wie ViewContainerRef laden können.

Elternteil

import { ViewChild, ElementRef, Component,OnInit, ComponentFactoryResolver } from '@angular/core';
import { LoadChildDirective } from './load.child.directive';

@Component({
  moduleId: module.id,
  selector: 'parent',
  template: `
    <StackLayout>
      <StackLayout *loadChild></StackLayout>
      <Button id="dfSaveButton" text="save"></Button>
    </StackLayout>
  `})
export class ParentComponent implements OnInit {

  constructor() {}
  ngOnInit() {
    console.log("In parent ngOnInit");
  }
}

Richtlinie zum Laden von Kindern

import { Directive, OnInit , ViewContainerRef, ComponentFactoryResolver } from "@angular/core";
import { ChildComponent } from './child.component';

@Directive({
  selector: "[loadChild]" // directive tag used in parent
})
export class LoadChildDirective implements OnInit {
  constructor(private vcRef: ViewContainerRef,
  private componentFactoryResolver: ComponentFactoryResolver
) {
    console.log("In loadChildDirective constructor with vcRef=" + vcRef);
  }

  ngOnInit() {
    console.log("In LoadChildDirective ngOnInit with vcRef=" + this.vcRef);
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(ChildComponent);
    const viewContainerRef = this.vcRef;
        viewContainerRef.clear();
    const componentRef = viewContainerRef.createComponent<ChildComponent>(componentFactory);
    const child = <ChildComponent>componentRef.instance;
  }
}

Verweise

https://angular.io/guide/dynamic-component-loader#dynamic-component-loading