Home > Back-end >  Ionic 6 - Viewchild null in dynamic component loader
Ionic 6 - Viewchild null in dynamic component loader

Time:05-18

I´m developing an Ionic application that will have a form with controls created dinamically from JSON data.

load-child.directive.ts:

import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[dynamicChildLoader]'
})
export class DynamicChildLoaderDirective {
  constructor(public viewContainerRef: ViewContainerRef) { }
}

app.module.ts:

...
@NgModule({
  declarations: [AppComponent, DynamicChildLoaderDirective],
...

home.page.html

...
<ion-content [fullscreen]="true" >
  
    <form [formGroup]="testForm">
      <ion-card *ngFor="let objGrupo of grupos">
        <ion-card-header>
          <ion-card-title>{{objGrupo.label}}</ion-card-title>
        </ion-card-header>
        
        <ion-card-content *ngFor="let objControl of objGrupo.controls">
          <app-dynamic-form [control]="objControl" [form]="testForm"></app-dynamic-form>
        </ion-card-content>    
      </ion-card>
      
    </form>
</ion-content>
...

home.page.ts:

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { DynamicFormGroup } from '../models/dynamic_form_group';
import { Ticket } from '../models/ticket';
import { TicketService } from '../services/ticket.service';


@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

  grupos: DynamicFormGroup[] = [];
  testControl: any = {};
  testForm: FormGroup;
  validation_messages: any;

  constructor(public formBuilder: FormBuilder,private ticketService: TicketService) {

    this.ticketService.obtenerFormularioTicket()
      .subscribe(obtainedInfo => {
        var ticket: Ticket = obtainedInfo;
        this.grupos = ticket.getFormList();
        this.testControl = this.grupos[0].controls;
      });
  }

}

dynamic-form.component.html:

<ion-label>{{ control.Name }}</ion-label>  

<ng-template dynamicChild></ng-template>

dynamic-form.component.ts:

import { Component, Input, OnInit, Type, ViewChild } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { DynamicChildLoaderDirective } from 'src/app/directives/load-child.directive';
import { DnElement } from '../dn-element/dn-element.component';
import { DnInputTextComponent } from '../dn-input-text/dn-input-text.component';
import { DnLabelComponent } from '../dn-label/dn-label.component';

@Component({
  selector: 'app-dynamic-form',
  templateUrl: './dynamic-form.component.html',
  styleUrls: ['./dynamic-form.component.scss'],
})
export class DynamicFormComponent implements OnInit {
  @ViewChild(DynamicChildLoaderDirective, { static: true }) dynamicChild!: DynamicChildLoaderDirective;
  @Input() control: any;
  @Input() form: FormGroup;
  componente: Type<any>;  
  
  ngOnInit(): void {
    this.loadDynamicComponent();
  }

  private loadDynamicComponent() {
    if(!this.control.Editable){
      this.componente = DnLabelComponent;
    }else{
      this.componente = DnInputTextComponent;
    }  
    const componentRef = this.dynamicChild.viewContainerRef.createComponent<DnElement>(this.componente);
    componentRef.instance.objControl = this.control;
    componentRef.instance.form = this.form;
  }
}

The problem is when running the application, the line

const componentRef = this.dynamicChild.viewContainerRef.createComponent<DnElement>(this.componente);

returns the following error:

ERROR Error: Uncaught (in promise): TypeError: this.dynamicChild is undefined

I´m using the following versions:

Ionic Version: 6.1.5

Angular Version: 13.3.5

I´m Using the offical angular docs as guide:

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

I´ve tried to change the static property of the ViewChild to false and move the ngOnInit to the ngAfterViewInit with the same result.

Is there anything missing here?

Thanks in advance.

Edit:

I´ve also tried with viewChildren:

export class DynamicFormComponent implements OnInit {
  @ViewChild(DynamicChildLoaderDirective, { static: true }) dynamicChild!: DynamicChildLoaderDirective;
  @ViewChildren(DynamicChildLoaderDirective) viewChildren!: QueryList<DynamicChildLoaderDirective>;
  @Input() control: any;
  @Input() form: FormGroup;
  componente: Type<any>;  
  
  ngOnInit(): void {

    //this.loadDynamicComponent();
  }

  ngAfterViewInit() {
    // viewChildren is set
    this.viewChildren.changes.subscribe((r) => {
      console.log(this.viewChildren);
    });

  }

No error presents but it never enters the console.log part of the subscribe.

CodePudding user response:

You should call template references when your page fully loaded and for that We have a lifecycle hooks triggered when your page fully loaded:

ngAfterViewInit(){
    this.loadDynamicComponent();
}

CodePudding user response:

try this

ngAfterContentInit(){
  this.loadDynamicComponent();
}
  • Related