我正在开发一个Ionic应用程序,它将有一个从JSON数据动态创建的带有控件的表单。
load-child.directive.ts:
import { Directive, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[dynamicChild]'
})
export class DynamicChildLoaderDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
app.module.ts:
...
@NgModule({
declarations: [AppComponent, DynamicChildLoaderDirective],
...
home.page.html
...
<ion-content [fullscreen]="true" class="ion-padding">
<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;
}
}
问题是在运行应用程序时,
行const componentRef = this.dynamicChild.viewContainerRef.createComponent<DnElement>(this.componente);
返回以下错误:
ERROR Error: Uncaught (in promise): TypeError: this.dynamicChild is undefined
我正在使用以下版本:
Ionic Version: 6.1.5
Angular版本:13.3.5我使用angular官方文档作为指南:
https://angular.io/guide/dynamic-component-loader
我试图改变ViewChild的静态属性为false,并将ngOnInit移动到ngAfterViewInit,结果相同。
这里少了什么吗?
提前感谢。
编辑:
我也试过使用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);
});
}
没有出现错误,但它从未进入订阅的console.log部分。
最后我设法使它工作。代码是可以的,不同之处在于指令的声明必须在我使用它的页面中完成。在本例中,在home.module.ts而不是app.module.ts:
...
@NgModule({
imports: [
CommonModule,
FormsModule,ReactiveFormsModule,
IonicModule,
HomePageRoutingModule
],
declarations: [HomePage,DynamicFormComponent,DynamicChildLoaderDirective]
})
...
你应该在页面完全加载时调用模板引用,因此我们有一个生命周期钩子在页面完全加载时触发:
ngAfterViewInit(){
this.loadDynamicComponent();
}
try this
ngAfterContentInit(){
this.loadDynamicComponent();
}