You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 11:06:33