我正在处理一个角度应用程序,下面的问题是使用@Input装饰器将数据从父组件传递到子组件
我有一个名为PatientDetailsComponent.的父组件这是该组件的TypeScript代码:
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { SelectItem } from 'primeng/api';
import { Observable } from 'rxjs';
import { PatientService } from 'src/app/services/patient.service';
import { Patient } from 'src/app/shared/interfaces/patient';
@Component({
selector: 'app-patient-details',
templateUrl: './patient-details.component.html',
styleUrls: ['./patient-details.component.scss']
})
export class PatientDetailsComponent implements OnInit, OnDestroy {
patientUID: string;
private sub: any;
public patient: Patient;
public patient$: Observable<Patient>;
editPatientOption: SelectItem[];
editPatientSelectedOption: string = "info";
selectedEditPatientId: string;
constructor(
private route: ActivatedRoute,
private patientService: PatientService
) { }
ngOnInit(): void {
this.sub = this.route.params.subscribe(params => {
this.patientUID = params['id'];
this.editPatientOption = [{label: 'Info', value: 'info'}, {label: 'Edit', value: 'edit'}];
console.log("RETRIEVED PATIENT UID ENTERING IN PatientDetailsComponent: " + this.patientUID);
//this.loadPatientDetails();
this.patientService.getPatientByUID(this.patientUID).subscribe(patient => {
console.log("RETRIEVED PATIENT: ", patient);
this.patient = patient;
});
});
}
ngOnDestroy() {
this.sub.unsubscribe();
}
// Load the current patient details:
/*
async loadPatientDetails() {
this.patient$ = await this.patientService.getPatientByUID(this.patientUID)
console.log("patient$: ", this.patient$);
//this.loading = false;
}
*/
editPatientOptionOnChange(event, patientId) {
console.log("editPatientOptionOnChange START, event: ", event);
console.log("Patient ID: ", patientId);
this.selectedEditPatientId = patientId;
}
}正如您在这个父组件的ngOnInit()方法中所看到的,我正在调用一个服务方法,从Firestore检索患者信息,这个方法:
this.patientService.getPatientByUID(this.patientUID).subscribe(patient => {
console.log("RETRIEVED PATIENT: ", patient);
this.patient = patient;
});此方法使用检索到的值设置this.patient字段。必须将检索到的对象传递给子组件。
这是我的父组件的HTML代码:
<div class="container">
<p-selectButton [options]="editPatientOption"
[(ngModel)]="editPatientSelectedOption"
(onChange)="editPatientOptionOnChange($event, 5)"></p-selectButton>
<!--<div>{{(patient$ | async)?.completeName}}</div>-->
<div *ngIf="editPatientSelectedOption=='info';then info_content else edit_content">here is ignored</div>
<ng-template #info_content>
<app-patient-details-info [patientDetails]="patient"></app-patient-details-info>
</ng-template>
<ng-template #edit_content>
<app-patient-details-edit [patientDetails]="patient"></app-patient-details-edit>
</ng-template>
</div>如您所见,基于p-selectButton select所做的选择,它将被呈现为两个子组件之一。对于这两种情况,我都将patient对象作为子组件的@Input传递。
例如,这是第一个子组件的TypeScript代码:
import { AfterViewInit, Component, Input, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { Patient } from 'src/app/shared/interfaces/patient';
@Component({
selector: 'app-patient-details-info',
templateUrl: './patient-details-info.component.html',
styleUrls: ['./patient-details-info.component.scss']
})
export class PatientDetailsInfoComponent implements OnInit, AfterViewInit {
@Input()
patientDetails: any
patientDetail: Patient;
disabled = true;
constructor() { }
ngOnInit(): void {
console.log("PATIENT DETAILS: ", this.patientDetails);
}
ngAfterViewInit(): void {
console.log("ngAfterViewInit() START !!!");
}
}为了检索父组件传递的对象,我有这一行:
@Input()
patientDetails: any现在,这个子组件的HTML代码包含:
<div class="row">
<div class="col-2">
<p>ID Ordine</p>
</div>
<div class="col-10">
<input id="disabled-input" type="text" pInputText [disabled]="disabled" [(ngModel)]="patientDetails.completeName" />
</div>
</div>如您所见,我声明了一个输入标记,其值来自父组件传递给该子组件的模型对象。
这里有一个问题:在Chrome控制台中,我获得了以下输出,其中包含如下错误:
core.js:4442 ERROR TypeError: Cannot read property 'completeName' of undefined
at PatientDetailsInfoComponent_Template (patient-details-info.component.html:17)
at executeTemplate (core.js:7457)
at refreshView (core.js:7326)
at refreshComponent (core.js:8473)
at refreshChildComponents (core.js:7132)
at refreshView (core.js:7376)
at refreshEmbeddedViews (core.js:8427)
at refreshView (core.js:7350)
at refreshComponent (core.js:8473)
at refreshChildComponents (core.js:7132)在这些错误发生后,将console.log()的输出输出打印到子组件ngOnInit()方法中,该方法将打印检索到的对象:
{id: "RmGajJcqcKrqGKXKKvu3", firstName: "Andrea", surname: "Nobili", completeName: "Andrea Nobili", birthDate: t, …}这些信息被正确地呈现到我的页面中:

我怀疑问题在于,当加载子组件时,从Firstore检索数据的服务方法尚未检索数据,所以在开始时,它尝试将这一行呈现到子组件中:
<input id="disabled-input" type="text" pInputText [disabled]="disabled" [(ngModel)]="patientDetails.completeName" />但是completeName最初是没有定义的。然后,当从DB中完全检索对象时,我可以访问这个值。
我的代码有什么问题?我遗漏了什么?我如何纠正这种错误的行为,避免所有这些错误在我的Chrome控制台?
发布于 2020-12-14 21:01:35
是的,这是时间问题。子程序将在(异步) http请求完成之前加载。
您有几个选项可以处理这个问题:
将子程序中的代码包含在父类中的*ngIf="patientDetails".
*ngIf中(在父模板中,您可以决定哪个模板是有效的(一个空的病人))。无论如何,我建议为您的模型定义一个接口,并避免使用: any。这将为您提供类型转换和自动完成(假设Visual代码有正确的扩展,也可以使用html )。
发布于 2020-12-14 16:31:04
我猜和你一样:
I怀疑问题在于,当加载子组件时,从Firstore检索数据的服务方法尚未检索数据,所以在开始时,它尝试将这一行呈现到子组件中:
可能发生的情况是,异步任务是在之后完成的,是PatientDetailsInfoComponent的实例化。您可以尝试为您的子组件中的@Input()装饰器使用setter函数。通过此更改,patientDetails变量的值将在每个生命周期中更新,而不是只更新一次:
private _patientDetails: any;
@Input() set patientDetails(value: any) {
this._patientDetails = value;
}
get patientDetails(): any {
return this._patientDetails;
}https://stackoverflow.com/questions/65292292
复制相似问题