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等字段处:
- 首次进入页面时,
sizes/colors/genders等下拉选项数组可能未完成初始化,导致find方法返回undefined,加上非空断言!强制访问id就会抛出错误;刷新页面时这些数组可能已缓存或提前加载完成,因此不会报错。 - 若
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
相关产品推荐
相关产品推荐

