Angular使用dom-to-pdf导出*ngFor动态内容生成空白PDF如何解决
问题原因
你推测的原因正确:Angular的变更检测是异步执行的,你给records赋值后,*ngFor对应的DOM节点还没完成渲染,此时调用domToPdf抓取到的是空的DOM结构,所以导出的PDF是空白的。另外你的模板代码里存在变量名错误,也会导致渲染异常。
解决方案
步骤1:修复模板变量错误
你的模板中遍历体重数据的部分,循环变量名和调用名不一致,修改如下:
<!-- 原来的错误写法 --> <!-- <p *ngFor="let bloodPressure of record.weights">At {{weight.ReadTime | date:'h:mm a'}}, I weighed {{weight.Weight}}.</p> --> <!-- 修改为 --> <p *ngFor="let weight of record.weights">At {{weight.ReadTime | date:'h:mm a'}}, I weighed {{weight.Weight}}.</p>
步骤2:等待DOM渲染完成后再导出PDF
在组件中注入ChangeDetectorRef,手动触发变更检测后,将导出逻辑放到下一个事件循环执行,确保DOM已经渲染完成。
修改后的方法代码如下:
// 首先在组件构造函数中注入ChangeDetectorRef import { ChangeDetectorRef } from '@angular/core'; constructor(private cdr: ChangeDetectorRef, /* 其他原有依赖 */) {} generateReport(): void { let bloodPressureFilter = `${this.bloodPressure.readDateFrom ? `ReadDate_GreaterEqual=${encodeURIComponent(this.datePipe.transform(this.bloodPressure.readDateFrom, 'YYYY-MM-dd'))}` : ''}`; bloodPressureFilter = `${bloodPressureFilter ? '&' : ''}${this.bloodPressure.readDateTo ? `ReadDate_LessEqual=${encodeURIComponent(this.datePipe.transform(this.bloodPressure.readDateTo, 'YYYY-MM-dd'))}` : ''}`; let sugarFilter = `${this.sugar.readDateFrom ? `ReadDate_GreaterEqual=${encodeURIComponent(this.datePipe.transform(this.sugar.readDateFrom, 'YYYY-MM-dd'))}` : ''}`; sugarFilter = `${sugarFilter ? '&' : ''}${this.sugar.readDateTo ? `ReadDate_LessEqual=${encodeURIComponent(this.datePipe.transform(this.sugar.readDateTo, 'YYYY-MM-dd'))}` : ''}`; let weightFilter = `${this.weight.readDateFrom ? `ReadDate_GreaterEqual=${encodeURIComponent(this.datePipe.transform(this.weight.readDateFrom, 'YYYY-MM-dd'))}` : ''}`; weightFilter = `${weightFilter ? '&' : ''}${this.weight.readDateTo ? `ReadDate_LessEqual=${encodeURIComponent(this.datePipe.transform(this.weight.readDateTo, 'YYYY-MM-dd'))}` : ''}`; forkJoin({ bloodPressures: this.bloodPressureService.query(bloodPressureFilter), sugars: this.sugarService.query(sugarFilter), weight: this.weightService.query(weightFilter) }).subscribe(results => { this.records = []; const bloodPressures = this.bloodPressure.visible ? results.bloodPressures : []; const sugars = this.sugar.visible ? results.sugars : []; const weights = this.weight.visible ? results.weight : []; bloodPressures.forEach(bloodPressure => { const grouppedObject = this.records.find(record => record.entryDate === bloodPressure.ReadDate); if (!grouppedObject) { this.records.push({ entryDate: bloodPressure.ReadDate, bloodPressures: [ bloodPressure ] }); } else { grouppedObject.bloodPressures = grouppedObject.bloodPressures ?? []; grouppedObject.bloodPressures.push(bloodPressure); } }); sugars.forEach(sugar => { const grouppedObject = this.records.find(record => record.entryDate === sugar.ReadDate); if (!grouppedObject) { this.records.push({ entryDate: sugar.ReadDate, sugars: [ sugar ] }); } else { grouppedObject.sugars = grouppedObject.sugars ?? []; grouppedObject.sugars.push(sugar); } }); weights.forEach(weight => { const grouppedObject = this.records.find(record => record.entryDate === weight.ReadDate); if (!grouppedObject) { this.records.push({ entryDate: weight.ReadDate, weights: [ weight ] }); } else { grouppedObject.weights = grouppedObject.weights ?? []; grouppedObject.weights.push(weight); } }); // 手动触发变更检测,通知Angular更新DOM this.cdr.detectChanges(); // 将导出逻辑放到setTimeout中,等待DOM渲染完成 setTimeout(() => { const today = new Date(); const element = document.getElementById('report'); const options = { filename: `${this.datePipe.transform(today, 'yMMdhhmmssa')}.pdf` }; domToPdf(element, options, () => { console.log('PDF exported'); this.records = []; }); }, 0); }); }
如果上述方案还是有问题,可以把setTimeout的延时调整为100ms,适配部分复杂DOM的渲染耗时。
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

