编辑:我在这里发布的代码可以像我最初编写的那样正常工作。我的学生界面中有一个愚蠢的错字,因此对于每个学生对象,始终未定义值“ ID”。我很生气,花了我几天的时间才意识到这一点。谢谢大家的回答。
我目前正在学习Angular,但是在组件的OnInit方法中设置属性时遇到问题。我正在使用服务组件从json文件中获取数据,并将其分配给名为“ student”的自定义界面的实例。
当以数组的形式获取所有学生数据以显示在表格中时,一切都将正常运行。但是,当尝试仅获取一个学生的信息以显示在“详细”模板上时,出现“ TypeError:_co.student未定义”。
在模板中添加“ * ngIf ='student'”可消除错误消息,但不会填充模板。
这是获取服务组件中定义的数据的功能
private studentURL = 'assets/college-apps.json';
constructor(private http: HttpClient){}
getStudents(): Observable<Student[]> {
return this.http.get<Student[]>(this.studentURL);
}
getStudent(id: number): Observable<Student> {
return this.getStudents().pipe(map(students => students.find(student => student.ID === id)));
}
这是学生详细信息组件中的ngOnInit方法
student: Student;
constructor(private route: ActivatedRoute, private router: Router, private studentService: StudentService) {}
ngOnInit() {
let id = +this.route.snapshot.paramMap.get('id');
this.studentService.getStudent(id).subscribe(
student => this.student = student
)
}
这是模板
<mat-card *ngIf='student'>
<mat-card-title>{{student.firstName}} {{student.lastName}}'s Details</mat-card-title>
<mat-card-content let stude>First Name: {{student.firstName}} </mat-card-content>
<mat-card-content>Last Name: {{student.lastName}} </mat-card-content>
<mat-card-content>High School: {{student.highSchool}} </mat-card-content>
<mat-card-content>GPA: {{student.gpa}} </mat-card-content>
<mat-card-content>Reading SAT Score: {{student.readingSatScore}} </mat-card-content>
<mat-card-content>Math SAT Score: {{student.mathSatScore}} </mat-card-content>
<a mat-stroked-button [routerLink]="['']">Back</a>
</mat-card>
答案 0 :(得分:0)
由于this.student
是在未定义{第一次} this.student
时在异步回调(在订阅中)中分配的,因此模板是第一次分配。当http调用返回一个值时,this.student
得到一个值,直到那时angular尝试渲染模板时,它将产生未定义的错误。要解决这种情况,angular拥有一个名为safe navigation operator的运算符。
Angular安全导航运算符?防止null和 模板的属性路径中未定义的值。
<mat-card>
<mat-card-title>{{student?.firstName}} {{student?.lastName}}'s Details</mat-card-title>
<mat-card-content>First Name: {{student?.firstName}} </mat-card-content>
<mat-card-content>Last Name: {{student?.lastName}} </mat-card-content>
<mat-card-content>High School: {{student?.highSchool}} </mat-card-content>
<mat-card-content>GPA: {{student?.gpa}} </mat-card-content>
<mat-card-content>Reading SAT Score: {{student?.readingSatScore}} </mat-card-content>
<mat-card-content>Math SAT Score: {{student?.mathSatScore}} </mat-card-content>
<a mat-stroked-button [routerLink]="['']">Back</a>
</mat-card>
这是https://stackblitz.com/edit/angular-gem5sk概念的简单演示
尝试删除模板中的?
之一,您将在控制台中看到未定义的错误。
还应注意,直到http调用完成并且分配给模板中this.student
的相关位置的值将为空,这可能会带来糟糕的用户体验,尤其是对于持久的http调用。因为在应用程序中必须包含某种加载器机制,但这与该问题密切相关。在这里写只是作为补充信息。
还将console.log
和async块一起放置,以正确查看是否从http调用返回了值
ngOnInit() {
let id = +this.route.snapshot.paramMap.get('id');
this.studentService.getStudent(id).subscribe(student => {
this.student = student
console.log(this.student); // prints when the request completes
});
console.log(this.student); // always prints undefined, and gets printed before above console.log
}
答案 1 :(得分:0)
您可以尝试将类变量重命名为student$
($表示异步值)
然后更改您的mat-card
标签:
<mat-card *ngIf="student$ | async as student>
...
</mat-card>
答案 2 :(得分:-2)
您有两个问题:
1)student
最初是未定义的,这会导致错误。
您通过在模板中添加*ngIf="student"
来正确地解决了该问题,但随后遇到了第二个问题。
2)您的页面未使用最新值更新。
之所以会发生这种情况,是因为您没有在this.student
中设置ngOnInit
,而是通过一段时间后的回调进行设置的(特别是在网络请求完成后)。由于此代码发生在“未知”时间,因此不会通知Angular数据已更改,并且需要重新构建视图。
因此,您只需要手动通知Angular“嘿,某些更改”:
import { ChangeDetectorRef } from "@angular/core";
[...]
constructor(private route: ActivatedRoute,
private router: Router,
private studentService: StudentService,
private changeDetector: ChangeDetectorRef) {}
ngOnInit() {
let id = +this.route.snapshot.paramMap.get('id');
// Start network request
this.studentService.getStudent(id).subscribe(
student => {
// Run after the request is completed
this.student = student;
console.log(this.student);
this.changeDetector.markForCheck();
}
);
// Run immediately after starting the network request
console.log(this.student); // Undefined because the request hasn't completed yet
}