首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当子组件试图呈现父组件中检索的对象的值时,为什么要获得这种奇怪的行为?

当子组件试图呈现父组件中检索的对象的值时,为什么要获得这种奇怪的行为?
EN

Stack Overflow用户
提问于 2020-12-14 16:06:01
回答 2查看 222关注 0票数 0

我正在处理一个角度应用程序,下面的问题是使用@Input装饰器将数据从父组件传递到子组件

我有一个名为PatientDetailsComponent.的父组件这是该组件的TypeScript代码:

代码语言:javascript
复制
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检索患者信息,这个方法:

代码语言:javascript
复制
  this.patientService.getPatientByUID(this.patientUID).subscribe(patient => {
    console.log("RETRIEVED PATIENT: ", patient);
    this.patient = patient;
  });

此方法使用检索到的值设置this.patient字段。必须将检索到的对象传递给子组件。

这是我的父组件的HTML代码:

代码语言:javascript
复制
<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代码:

代码语言:javascript
复制
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 !!!");


  }

}

为了检索父组件传递的对象,我有这一行:

代码语言:javascript
复制
@Input()
patientDetails: any

现在,这个子组件的HTML代码包含:

代码语言:javascript
复制
<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控制台中,我获得了以下输出,其中包含如下错误:

代码语言:javascript
复制
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()方法中,该方法将打印检索到的对象:

代码语言:javascript
复制
{id: "RmGajJcqcKrqGKXKKvu3", firstName: "Andrea", surname: "Nobili", completeName: "Andrea Nobili", birthDate: t, …}

这些信息被正确地呈现到我的页面中:

我怀疑问题在于,当加载子组件时,从Firstore检索数据的服务方法尚未检索数据,所以在开始时,它尝试将这一行呈现到子组件中:

代码语言:javascript
复制
 <input id="disabled-input" type="text" pInputText [disabled]="disabled" [(ngModel)]="patientDetails.completeName" />

但是completeName最初是没有定义的。然后,当从DB中完全检索对象时,我可以访问这个值。

我的代码有什么问题?我遗漏了什么?我如何纠正这种错误的行为,避免所有这些错误在我的Chrome控制台?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-12-14 21:01:35

是的,这是时间问题。子程序将在(异步) http请求完成之前加载。

您有几个选项可以处理这个问题:

将子程序中的代码包含在父类中的*ngIf="patientDetails".

  • Extend display).

  • Define *ngIf中(在父模板中,您可以决定哪个模板是有效的(一个空的病人))。

无论如何,我建议为您的模型定义一个接口,并避免使用: any。这将为您提供类型转换和自动完成(假设Visual代码有正确的扩展,也可以使用html )。

票数 1
EN

Stack Overflow用户

发布于 2020-12-14 16:31:04

我猜和你一样:

I怀疑问题在于,当加载子组件时,从Firstore检索数据的服务方法尚未检索数据,所以在开始时,它尝试将这一行呈现到子组件中:

可能发生的情况是,异步任务是在之后完成的,PatientDetailsInfoComponent的实例化。您可以尝试为您的子组件中的@Input()装饰器使用setter函数。通过此更改,patientDetails变量的值将在每个生命周期中更新,而不是只更新一次:

代码语言:javascript
复制
private _patientDetails: any;
@Input() set patientDetails(value: any) {
  this._patientDetails = value;
}
get patientDetails(): any {
  return this._patientDetails;
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65292292

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档