Angular调用Get接口后*ngFor迭代正常但绑定数据为空问题
问题描述
Angular与TypeScript初学者调用Get接口拉取员工列表展示时出现异常:*ngFor可按照接口返回数组长度正确迭代生成对应数量的表格行,但行内通过插值表达式绑定的员工属性值全部为空,异常效果见截图:空值表格展示截图。
控制台打印接口返回数据确认:接口返回长度为8的员工数组,数组元素采用小驼峰命名:employeeID、employeeName、employeeSalary、intendedDate,各字段值均正常,控制台输出如下:
Array(8) 0: {employeeID: 1, employeeName: 'Taha BABI', employeeSalary: 400000, intendedDate: '2022-07-02T19:15:39.68'} 1: {employeeID: 2, employeeName: 'Aya', employeeSalary: 5000000, intendedDate: '2022-01-06T00:00:00'} 2: {employeeID: 4, employeeName: 'Rami', employeeSalary: 1000000, intendedDate: '2022-07-02T19:16:27.095'} 3: {employeeID: 5, employeeName: 'Rami', employeeSalary: 1000000, intendedDate: '2022-07-02T19:16:27.095'} 4: {employeeID: 6, employeeName: 'Rami', employeeSalary: 1000000, intendedDate: '2022-07-02T19:16:27.095'} 5: {employeeID: 7, employeeName: 'Rami', employeeSalary: 1000000, intendedDate: '2022-07-02T19:16:27.095'} 6: {employeeID: 8, employeeName: 'Ahmad BABI', employeeSalary: 1000000, intendedDate: '2022-07-04T00:00:00'} 7: {employeeID: 9, employeeName: 'Adnan', employeeSalary: 500000, intendedDate: '2022-01-01T00:00:00'} length: 8 [[Prototype]]: Array(0)
关联项目代码
Employee实体类定义
export class Employee { public EmployeeID: number; public EmployeeName: string; public EmployeeSalary: number; public IntendedDate: Date; }
EmployeeService服务类
import { Injectable } from "@angular/core" import { Employee } from "./Employee.Module" import { HttpClient } from "@angular/common/http" @Injectable({ providedIn: 'root' }) export class EmployeeService { constructor(private http:HttpClient) { } employeeFormData: Employee = new Employee(); public employeesList: Employee[]; readonly url = "https://localhost:7075/api/employee"; PostEmployeeData() { return this.http.post(this.url, this.employeeFormData +"/insert") } RefreshEmployeesList() { this.http.get(this.url).toPromise().then(res => { this.employeesList = res as Employee[]; console.log(this.employeesList); }); } }
employee.component.ts组件代码
import { Component, OnInit } from "@angular/core" import { EmployeeService } from "../Shared/EmployeeService"; @Component({ selector: "Employee", templateUrl: "./Employee.Component.html", styleUrls: [ ] }) export class EmployeeComponent { constructor(public service: EmployeeService) { } ngOnInit() { //console.log(this.service.employeesList); this.service.RefreshEmployeesList(); console.log("init"); } }
employee.component.html模板代码
<div class="text-center jumbotron p-3 "> <h1>Employees CRUD</h1> </div> <div class="row"> <div class="col"> <EmployeeForm> Please Wait Form.... </EmployeeForm> </div> <div class="col"> <table class="table"> <thead class="thead-light"> <tr> <th>Employee Number</th> <th>Employee Name</th> <th>Employee Salary</th> <th>Employee Intended Date</th> <th></th> </tr> </thead> <tbody> <tr *ngFor="let employee of service.employeesList;"> <td>{{employee.EmployeeID}}</td> <td>{{employee.EmployeeName}}</td> <td>{{employee.EmployeeSalary}}</td> <td>{{employee.IntendedDate}}</td> <td></td> </tr> </tbody> </table> </div> </div>
根因分析
- 核心问题是字段名大小写不匹配:JavaScript/TypeScript是大小写敏感语言,
EmployeeID和employeeID是完全独立的两个属性。TypeScript的as类型断言仅在编译阶段做类型校验提示,不会在运行时修改对象结构、转换字段名。定义的Employee类使用大驼峰命名字段,但接口实际返回的是小驼峰字段,模板中绑定大驼峰属性时,实际拿到的接口返回对象上不存在对应属性,因此渲染为空值。 *ngFor能正确生成8行表格的原因:*ngFor仅根据数组长度生成DOM结构,遍历过程不校验数组元素内部的属性名,因此行数和接口返回数组长度一致,但内部属性访问不到导致内容为空。
排查思路
- 第一步:核对接口返回原始对象的字段名,与实体类定义、模板绑定的字段名是否完全一致,重点检查大小写、拼写错误。
- 第二步:明确类型断言的作用边界:
as Employee[]不会将接口返回的普通JavaScript对象自动转换为Employee类实例,也不会自动做字段名映射,仅用于告知TS编译器按照Employee类型做语法校验,运行时无任何实际转换逻辑。 - 第三步:验证变更检测逻辑:当前场景下数组长度能正确渲染,说明Angular变更检测已正常触发,无需额外手动触发变更检测。
解决方案
二选一即可:
方案1:统一字段命名为小驼峰(推荐,无额外转换逻辑,和接口返回保持一致)
- 修改Employee实体类字段为和接口一致的小驼峰命名:
export class Employee { public employeeID: number; public employeeName: string; public employeeSalary: number; public intendedDate: Date; }
- 修改模板中的插值绑定,对应使用小驼峰字段,可追加
date管道格式化日期显示:
<tr *ngFor="let employee of service.employeesList;"> <td>{{employee.employeeID}}</td> <td>{{employee.employeeName}}</td> <td>{{employee.employeeSalary}}</td> <td>{{employee.intendedDate | date:'yyyy-MM-dd HH:mm'}}</td> <td></td> </tr>
方案2:接口返回后手动映射字段(适用于前端必须保留大驼峰命名规范的场景)
修改RefreshEmployeesList方法,拿到接口返回数据后手动将小驼峰字段映射为大驼峰属性,同时可将日期字符串转为Date对象:
RefreshEmployeesList() { this.http.get<Array<any>>(this.url).toPromise().then(res => { this.employeesList = res.map(item => { const emp = new Employee(); emp.EmployeeID = item.employeeID; emp.EmployeeName = item.employeeName; emp.EmployeeSalary = item.employeeSalary; emp.IntendedDate = new Date(item.intendedDate); return emp; }); console.log(this.employeesList); }); }
该方案无需修改现有模板的大驼峰绑定逻辑。
额外代码优化建议
- 废弃
toPromise()写法:Angular HttpClient原生返回Observable对象,更符合Angular响应式编程范式,变更检测触发逻辑更稳定,改造示例:
// 服务层方法改造 import { map } from 'rxjs/operators'; RefreshEmployeesList() { return this.http.get<Array<any>>(this.url).pipe( map(res => res.map(item => ({ employeeID: item.employeeID, employeeName: item.employeeName, employeeSalary: item.employeeSalary, intendedDate: new Date(item.intendedDate) }))) ); } // 组件层订阅赋值 ngOnInit() { this.service.RefreshEmployeesList().subscribe(list => { this.service.employeesList = list; }); }
- 修复Post请求路径拼接错误:现有
PostEmployeeData方法中this.employeeFormData +"/insert"会将表单对象转为字符串后拼接URL,导致请求地址异常,修改为:
PostEmployeeData() { return this.http.post(`${this.url}/insert`, this.employeeFormData) }
内容的提问来源于stack exchange,提问作者Taha Babi
相关产品推荐
相关产品推荐

