Come faccio a caricare un componente figlio Angular Nativescript utilizzando code behind?
Sto provando ad aggiungere un componente angolare figlio personalizzato a un GridLayout che è definito nel modello del genitore. Il genitore deve supportare alcuni componenti con cui sa come costruire e con cui interagire, ma deve essere in grado di posizionarli quando e dove indicato all'interno di GridLayout.
- Posso specificare il mio figlio SampleComponent all'interno del modello e viene visualizzato.
- Se provo ad aggiungere il mio SampleComponent a GridLayout utilizzando il codice sottostante, la logica grid.addChildChild non fornisce errori, ma il componente non viene visualizzato.
- Se aggiungo un pulsante alla griglia utilizzando il codice sottostante, viene visualizzato come previsto.
Capisco che sto cercando di caricare un componente rispetto a un pulsante, ma il mio componente estende ContentView. Ho visto alcune discussioni su Builders, ma sembrava che stessero compilando dal codice sorgente, dove i miei componenti figlio sono costruiti con il mio genitore e usano modelli .xml. Ho provato a guardare ComponentBuilder, ma non riesco a trovare alcuna documentazione che mi aiuti a capire come potrei usarlo.
Le 3 funzioni più rilevanti del mio campione sono di seguito, avviate dall'utente digitando "campione" o "pulsante" e facendo clic su AGGIUNGI per attivare il tocco () .:
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;
}
Risposte
Le chiavi per far funzionare questo utilizzando un paradigma angolare NativeScript sembrano essere le direttive e l'iniezione.
La mia soluzione è usare una direttiva per caricare la mia direttiva figlio. La direttiva usa l'iniezione per ottenere i riferimenti necessari per costruire il componente figlio nella vista del genitore.
Quindi, nel mio esempio qui sotto,
<StackLayout *LoadChildComponent></StackLayout>
attiva il mio LoadChildDirective, ma avvolge anche l'elemento stack
<ng-template loadChild><StackLayout></StackLayout></ng-template>
Ciò significa che il contesto di visualizzazione della direttiva è la posizione appena fuori dall'elemento stack.
- Come i componenti, la direttiva può utilizzare l'iniezione per ottenere cose interessanti che ci consentono di caricare un componente figlio come ViewContainerRef.
Genitore
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");
}
}
Direttiva sul carico di bambini
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;
}
}
Riferimenti
https://angular.io/guide/dynamic-component-loader#dynamic-component-loading