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

Angular页面跳转初始化表单报错:TypeError: 无法读取undefined的'id'

解决编辑产品页面首次加载时表单赋值的Cannot read properties of undefined (reading 'id')错误

问题描述

跳转至编辑产品页面时,产品未完成加载,出现错误:ERROR TypeError: Cannot read properties of undefined (reading 'id')。当前情况:通过shopService根据URL中的ID从API获取产品数据,进入页面时产品数据(如照片)可正常显示,但初始化表单值时触发上述错误;刷新页面后表单可正常加载数据,仅首次进入页面时表单无数据。

相关代码

export class EditProductComponent implements OnInit {
  product: IProduct;
  uploader: FileUploader;
  hasBaseDropZoneOver = false;
  baseUrl = environment.apiUrl;
  currentMain!: Photo;
  response!:string;
  photos: PhotoPicture[] = [];
  productForm:FormGroup;

  constructor(private shopService: ShopService, private activateRoute: ActivatedRoute, 
    private bcService: BreadcrumbService,private fb: FormBuilder,
    private http:HttpClient, private router: Router) {
    }

  ngOnInit(): void {
    this.createAddProductForm();
    this.loadProduct();
  }

  loadProduct() {
    console.log(+this.activateRoute.snapshot.paramMap.get('id')!);
    this.shopService.getProduct(+this.activateRoute.snapshot.paramMap.get('id')!).subscribe(product => {
      console.log(product);
      this.product = product;
      this.photos = product.pictures;
      console.log(this.photos);
      this.bcService.set('@productDetails', product.name)

      this.uploader = new FileUploader({
        url: this.baseUrl + 'photos/'+product.id  + '/photos',
        authToken: 'Bearer ' + localStorage.getItem('token'),
        isHTML5: true,
        allowedFileType: ['image'],
        removeAfterUpload: true,
        autoUpload: false,
        maxFileSize: 10 * 1024 * 1024 
      });
      console.log(this.productForm);
      if(product)
      {
        this.productForm.setValue({name: product.name, 
          description: product.description,
          id:product.id
        ,price: product.price, 
        quantity:product.quantity, 
        size:product.productSize  ,productSizeId: this.sizes.find(p => p.name == product.productSize)!.id  
        ,color: product.productColor, productColorId: this.colors.find(p => p.name == product.productColor)!.id 
       ,gender : product.productGender, productGenderId: this.genders.find(p => p.name == product.productGender)!.id,
         fit: product.productFit, productfitId:this.fits.find(p => p.name == product.productFit)!.id,
        type: product.productType, producttypeId: this.types.find(p => p.name == product.productType)!.id,
        brand:product.productBrand, productbrandId: this.brands.find(p => p.name == product.productBrand)!.id});
      }
    }, error => {
      console.log(error);
    });
  }
}

问题根源

错误触发点在表单赋值的productSizeId、productColorId等字段处:

  1. 首次进入页面时,sizes/colors/genders等下拉选项数组可能未完成初始化,导致find方法返回undefined,加上非空断言!强制访问id就会抛出错误;刷新页面时这些数组可能已缓存或提前加载完成,因此不会报错。
  2. 若product的属性(如productSize)为空,find方法也会返回undefined,访问id时触发错误。

解决方案

1. 提前加载下拉选项数据

确保size/color等选项数组在产品数据加载前完成初始化,避免异步加载顺序问题:

ngOnInit(): void {
  this.createAddProductForm();
  // 先加载所有下拉选项,再加载产品数据
  this.loadDropdownOptions().then(() => {
    this.loadProduct();
  });
}

// 封装下拉选项加载逻辑
private loadDropdownOptions(): Promise<void> {
  return new Promise((resolve) => {
    const requests = [
      this.shopService.getSizes().subscribe(data => this.sizes = data),
      this.shopService.getColors().subscribe(data => this.colors = data),
      this.shopService.getGenders().subscribe(data => this.genders = data),
      this.shopService.getFits().subscribe(data => this.fits = data),
      this.shopService.getTypes().subscribe(data => this.types = data),
      this.shopService.getBrands().subscribe(data => this.brands = data)
    ];
    // 等所有请求完成后执行后续逻辑
    Observable.forkJoin(requests).subscribe(() => resolve());
  });
}

2. 移除非空断言,添加空值安全检查

不要依赖!强制断言结果存在,而是通过可选链或条件判断处理空值:

// 替换原有表单赋值代码
if(product) {
  this.productForm.patchValue({
    name: product.name,
    description: product.description,
    id: product.id,
    price: product.price,
    quantity: product.quantity,
    size: product.productSize,
    productSizeId: this.sizes.find(p => p.name == product.productSize)?.id || null,
    color: product.productColor,
    productColorId: this.colors.find(p => p.name == product.productColor)?.id || null,
    gender: product.productGender,
    productGenderId: this.genders.find(p => p.name == product.productGender)?.id || null,
    fit: product.productFit,
    productfitId: this.fits.find(p => p.name == product.productFit)?.id || null,
    type: product.productType,
    producttypeId: this.types.find(p => p.name == product.productType)?.id || null,
    brand: product.productBrand,
    productbrandId: this.brands.find(p => p.name == product.productBrand)?.id || null
  });
}

3. 使用patchValue替代setValue

setValue要求表单所有字段必须赋值,patchValue支持部分字段赋值,降低因字段缺失导致的报错风险,同时配合可选链操作符?.进一步避免空值访问错误。

内容的提问来源于stack exchange,提问作者Alejandro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 12:53:23