Angular首次请求报Cannot convert undefined/null to object修复
错误根因
报错本质是对Angular HttpClient的异步逻辑处理错误:
subscribe订阅的HTTP请求是异步非阻塞的,发起请求后代码会立刻往下执行,不会等待接口返回- 首次点击按钮时,接口还没返回结果,
this.Location的值是初始的undefined,执行Object.keys(this.Location)时传入了undefined,就会抛出Cannot convert undefined or null to object错误 - 第二次点击能正常运行,是因为第一次的异步请求已经执行完成,
this.Location已经被赋值,遍历逻辑可以正常执行 - 原有逻辑还有两个隐藏问题:一是每次调用请求都会重复往下拉数组里push数据,多次触发会导致选项重复;二是请求触发逻辑写反了,只有表单校验不通过的时候才会拉取地点数据,不符合交互逻辑。
修复方案
1. 修正组件异步逻辑(核心修复)
把所有依赖接口返回值的逻辑全部移到subscribe回调内,保证拿到数据后再做遍历赋值,同时修正触发逻辑、避免数据重复。修改后的home.component.ts代码如下:
import { Component, OnInit } from '@angular/core'; import { LocationService } from 'src/app/service/location.service'; @Component({ selector: 'app-home', templateUrl: './home.component.html', styleUrls: ['./home.component.css'] }) export class HomeComponent implements OnInit { // 初始化空数组存储下拉选项,移除硬编码初始值避免重复 stringifiedData: string[] = []; // 明确地点列表类型,替换any类型减少隐患 locationList: Array<{locationId: number, locationName: string}> = []; booking = { from: "", to: "", date: "" } fetchLocation() { this.GetRequest.fetchLocation().subscribe({ next: (data) => { this.locationList = data; // 赋值前先清空数组,避免多次调用重复追加选项 this.stringifiedData = []; // 确认拿到数据后再遍历 for (let i = 0; i < this.locationList.length; i++) { this.stringifiedData.push(this.locationList[i].locationName); } }, error: (err) => { console.error('地点数据加载失败:', err); alert('出发地选项加载失败,请刷新重试'); } }) } getBookingFormData() { if(!this.validateForm()) { return; } // 校验通过后再执行后续提交逻辑 console.log('表单提交数据:', this.booking); } validateForm() { if (this.booking.from == "" || this.booking.to == "" || this.booking.date == "") { alert("请填写所有字段"); return false; } else if (this.booking.from == this.booking.to) { alert("出发地和目的地不能相同"); return false; } else if (this.booking.date < new Date().toISOString().split('T')[0]) { alert("请选择未来的出行日期"); return false; } else { return true; } } constructor(private GetRequest : LocationService) { } // 页面初始化时就加载下拉数据,不需要等用户点击按钮再加载,提升交互体验 ngOnInit(): void { this.fetchLocation(); } }
2. 补全服务层类型定义
修正location.service.ts的类型声明,避免TS类型校验报错:
import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; // 定义和后端返回字段匹配的地点类型 interface Location { locationId: number; locationName: string; } @Injectable({ providedIn: 'root' }) export class LocationService { private baseUrl:string = "http://localhost:8080"; constructor(private http:HttpClient) { } fetchLocation() { return this.http.get<Location[]>(`${this.baseUrl}/getLocations`); } }
3. 模板优化(可选)
修复模板里的属性冲突,增加加载态提示,修改home.component.html的下拉框部分:
<!-- 出发地下拉框 --> <select class="select form-control-lg mx-5 my-5" id="fromLocation" [(ngModel)]="booking.from" name="fromLocation" [disabled]="stringifiedData.length === 0" > <option value="" disabled>请选择出发地</option> <option *ngFor="let country of stringifiedData" [value]="country"> {{ country }} </option> </select> <!-- 目的地下拉框,注意id/name不要和出发地重复,避免ngModel绑定冲突 --> <select class="select form-control-lg mx-5 my-2" id="toLocation" [(ngModel)]="booking.to" name="toLocation" > <option value="" disabled>请选择目的地</option> <option value="Mumbai">Mumbai</option> <option value="Delhi">Delhi</option> <option value="Kolkata">Kolkata</option> </select>
后端说明
现有Spring Boot端接口逻辑无需修改,只要保证服务正常启动、数据库存在地点数据、跨域配置生效即可正常返回结果。
修复效果
页面加载时会自动拉取地点数据填充下拉框,首次点击提交按钮不会再抛出空对象转换错误,也不会出现下拉选项重复累加的问题。
内容的提问来源于stack exchange,提问作者Jay Mistry
相关产品推荐
相关产品推荐

