Angular5绑定错误:Cannot read property 'size' of undefined 求助
解决Angular中TypeScript值绑定HTML时的"Cannot read property 'size' of undefined"错误
嗨,我来帮你拆解问题根源,并且给出针对性的修复方案:
问题核心原因
你遇到的报错主要来自三个关键点:
- 异步操作时序不匹配:
imageCompressor.compress()是异步Promise操作,当Angular首次渲染表格时,results[i]还没被异步回调填充,此时它是undefined,直接访问.size就会触发报错。 - var变量的闭包陷阱:循环里用
var i会导致所有异步回调共享同一个i变量,最终所有回调都会指向files.length这个越界索引,进一步加剧results[i]的undefined问题。 - 全局变量覆盖问题:
sizeRound是单个变量,处理多个文件时会被最后一次循环的结果覆盖,所有表格行都会显示同一个压缩比例。
分步修复方案
1. 修正TypeScript代码的异步与闭包问题
先把循环里的var改成let解决变量共享问题,同时新增数组存储每个文件的独立压缩比例,还要提前初始化数组长度避免索引越界:
// 新增数组存储每个文件的压缩比例 results: Blob[] = []; sizeRounds: string[] = []; changeInput() { this.img = <HTMLImageElement>document.querySelector('img,#image'); const inputElement = <HTMLInputElement>document.querySelector('input[type=file],#input'); this.files = inputElement.files; this.inputFlag = true; // 提前初始化数组长度,避免索引越界 this.results = new Array(this.files.length); this.sizeRounds = new Array(this.files.length); for (let i = 0; i < this.files.length; i++) { // 用let代替var,解决闭包陷阱 const currentFile = this.files[i]; const imageCompressor = new ImageCompressor(); imageCompressor.compress(currentFile, { quality: .4 }) .then((result) => { this.results[i] = result; // 用当前文件的原始大小计算压缩比例,避免依赖全局变量 const originalSize = currentFile.size; this.sizeRounds[i] = `${Math.round(100 - result.size * 100 / originalSize)}%`; }); } }
2. 修改HTML模板,安全访问异步数据
在HTML中使用**可选链操作符?.**来安全访问results[i].size,或者用||设置默认占位符,同时绑定对应索引的sizeRounds[i]:
<tr *ngFor="let file of files; let i = index"> <td style="height: 30px" class="text-center">{{file.name.slice(0,7)}}</td> <td style="height: 30px" class="text-center">{{status}}</td> <td style="height: 30px" class="text-center"></td> <td style="height: 30px" class="text-center">{{file.size}}</td> <!-- 可选链+默认值,避免undefined报错 --> <td style="height: 30px" class="text-center">{{results[i]?.size || '-'}}</td> <td style="height: 30px" class="text-center">{{sizeRounds[i] || '-'}}</td> </tr>
3. 进阶优化:用Angular规范方式获取DOM元素
尽量避免直接使用document.querySelector,改用Angular的@ViewChild获取DOM元素,更符合组件化思想:
首先在组件类中引入并声明视图引用:
import { ViewChild, ElementRef } from '@angular/core'; @ViewChild('inputFile') inputFile: ElementRef<HTMLInputElement>; @ViewChild('imageElement') imageElement: ElementRef<HTMLImageElement>;
然后修改HTML标签添加模板引用:
<input #inputFile type="file" accept="image/*" autocomplete="off" style="display: none" draggable="true" (change)='changeInput()' multiple> <!-- 给img标签也添加模板引用 --> <img #imageElement id="image" ...>
最后在changeInput中替换DOM获取方式:
this.img = this.imageElement.nativeElement; this.files = this.inputFile.nativeElement.files;
内容的提问来源于stack exchange,提问作者hrtlkr29
相关产品推荐
相关产品推荐

