Comment charger un composant enfant Nativescript Angular en utilisant le code derrière?

Oct 01 2020

J'essaie d'ajouter un composant angulaire enfant personnalisé à un GridLayout qui est défini dans le modèle du parent. Le parent doit prendre en charge quelques composants avec lesquels il sait construire et interagir, mais il doit être en mesure de les placer quand et où il est indiqué dans GridLayout.

  • Je peux spécifier mon enfant SampleComponent dans le modèle et il s'affiche.
  • Si j'essaie d'ajouter mon SampleComponent au GridLayout en utilisant le code derrière, la logique grid.addChildChild ne donne aucune erreur, mais le composant ne s'affiche pas.
  • Si j'ajoute un bouton à la grille en utilisant le code derrière, il s'affiche comme prévu.

Je comprends que j'essaie de charger un composant par rapport à un bouton, mais mon composant étend ContentView. J'ai vu quelques discussions sur les Builders, mais ils semblaient compiler à partir du code source, où mes composants enfants sont construits avec mon parent, et ils utilisent le modèle .xml. J'ai essayé de regarder ComponentBuilder, mais je ne trouve aucune documentation qui m'aide à comprendre comment je pourrais l'utiliser.

Les 3 fonctions les plus pertinentes de mon exemple sont ci-dessous, initiées par l'utilisateur saisissant `` échantillon '' ou `` bouton '' et cliquant sur AJOUTER pour lancer onTap ():

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

Réponses

JeffWheatley Oct 15 2020 at 19:50

Les clés pour que cela fonctionne en utilisant un paradigme Angular NativeScript semblent être les directives et l'injection.

Ma solution est d'utiliser une directive pour charger ma directive enfant. La directive utilise l'injection pour obtenir les références nécessaires pour créer le composant enfant dans la vue du parent.

Donc, dans mon exemple ci-dessous,

<StackLayout *LoadChildComponent></StackLayout>

déclenche mon LoadChildDirective, mais encapsule également l'élément stack

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

Cela signifie que le contexte de vue de la directive est la position juste à l'extérieur de l'élément de pile.

  • Comme les composants, la directive peut utiliser l'injection pour obtenir des éléments intéressants qui nous permettent de charger un composant enfant tel que ViewContainerRef.

Parent

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

Directive de charge enfant

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

Références

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