【问题标题】:*ngFor displaying data but throwing 'undefined' error*ngFor 显示数据但抛出“未定义”错误
【发布时间】:2019-05-06 07:31:12
【问题描述】:

我有一个 *ngFor 指令,它按预期运行并在我的浏览器中显示数据。尽管我的 chrome 控制台显示“未定义”错误。

http.service.ts:

getExerciseProgress(exId: number): Observable<Exercise> {
    return this.http.get<Exercise>(this.baseURL + 'exercises/GetProgressionByExerciseId/' + exId)
  }

view-exercise.component.ts:

 exercise: Exercise;

  constructor(private http: HttpService) { }


  ngOnInit() {
    this.http.getExerciseProgress(7).subscribe(ex =>{
      this.exercise = ex;
      console.log(this.exercise);
    });
  }

作为参数传递的 7 用于测试目的,当我记录结果时,该对象似乎就是我正在寻找的。一个带有嵌套 Progress 数组的练习对象。

view-exercise.component.html:

<p *ngFor="let p of exercise.progress">{{ p.bpm }}</p>

以上行是在我的 Chrome 控制台窗口中抛出以下消息的行。 “错误类型错误:无法读取未定义的属性‘进度’”。尽管如此,我的浏览器仍显示正确的信息。

客户端模型(以防万一):

export class Exercise {

id: number;
description: string;
progress: Progress[];

constructor(id: number, description: string, progress: Progress[]){
    this.id = id;
    this.description = description;
    this.progress = progress;
}

export class Progress {

id: number;
dateAttempted: Date;
bpm: number;

constructor(id: number, dateAttempted: Date, bpm: number){
    this.id = id;
    this.dateAttempted = dateAttempted;
    this.bpm = bpm;
}

}

提前致谢

【问题讨论】:

    标签: angular typescript ngfor


    【解决方案1】:

    你可以试试这个: &lt;p *ngFor="let p of exercise?.progress"&gt;{{ p.bpm }}&lt;/p&gt;

    我的猜测是,即使您的数据不是初始化的,Angular 也会首先尝试显示某些内容。

    【讨论】:

      【解决方案2】:

      这是一个竞争条件:当模板首次加载时,this.exercise 仍然是 undefined。然后,一旦 Observable 解析并分配了一个值,就会触发 change detection cycle,然后运行 ​​*ngFor 并且您会看到这些值。

      有两种典型的模式可以解决这个问题:

      使用elvis operator(在您的情况下可能是最好的,因为您只有一个访问器):

      <p *ngFor="let p of exercise?.progress">{{ p.bpm }}</p>
      

      ?. 表示“如果定义了左侧的操作数,则访问此运算符右侧的属性”。这么写,很明显是可以上链的:exercise?.progress?.timer?.started

      或者在容器元素上使用保护,当你有很多访问者并且不想在任何地方重复 ?. 时会更好:

      <ng-container *ngIf="exercise">
        <p *ngFor="let p of exercise.progress">{{ p.bpm }}</p>
      <ng-container>
      

      在上面的示例中,我使用了 &lt;ng-container /&gt;,因为它没有渲染到 DOM 中,但是您可以很容易地在真实元素上使用它,例如 &lt;div /&gt;。这通常用于*ngIf="exercise; else no-data" 模式,其中#no-data 是另一个 ng-template,它会在您加载数据时替换 div。


      FYI 旁注,因为 Angular 使用 polyfill,你可以在 TypeScript 中安全地使用 template strings。意思是,你可以写

      this.baseURL + 'exercises/GetProgressionByExerciseId/' + exId
      

      作为

      `${this.baseURL}/exercises/GetProgressionByExerciseId/${exId}`
      

      有些人觉得更容易阅读。

      【讨论】:

      • 成功了,非常感谢。正如你所描述的,我去了猫王接线员。我也很欣赏快速响应和额外的细节。
      • @Gilmo 很高兴我能帮上忙 :)
      【解决方案3】:

      您的 DOM 似乎在服务调用完成之前在练习中寻找进度对象,并且您有可用的数据。

      当它可用时你应该循环它,这意味着 API 调用已经完成并且你有数据:

      <p *ngFor="let p of exercise?.progress">{{ p.bpm }}</p>
      

      或者使用*ngIf来实现。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-28
        • 1970-01-01
        • 2021-12-21
        • 2014-05-30
        • 2014-07-09
        相关资源
        最近更新 更多