Angular行程生成器中对象与HTML时间值更新异常问题
行程生成器时间累加错误问题
我正在开发一个行程生成器,所有活动都有对应时长(分钟),行程设有开始时间。生成行程时需要根据活动时长更新时间,并将更新后的时间传递给后续活动展示。
期望效果:
若行程开始时间为下午6:00,第一个活动时长5分钟,HTML应展示:
- 06:00 第一个活动(5分钟)
- 06:05 第二个活动(2分钟)
- 06:07 第三个活动(10分钟)
实际错误效果:
时间未正确传递,出现重复累加:
- 06:05 第一个活动(5分钟)
- 06:07 第二个活动(2分钟)
- 06:17 第三个活动(10分钟)
相关代码
Activity接口
export interface Activity{ activityName : string; duration : number; zone : string; participants : Participante []; time ?: Date }
Program组件
export class ProgramComponent { program !: Program[] startTime !: Date; time !: Date; // 用于在父子组件间传递的时间 constructor( private gpService: ProgramGeneratorServices ) { // 从表单中保存开始时间,此处省略不必要的接口定义 this.startTime = this.program[0].startTime; this.time = this.program[0].startTime; } // 重置时间为开始时间,因需生成两周的行程,需在ngFor中重置 resetHour() { this.time = this.startTime } // 在Program组件内更新时间 updateMinutes( activity?: Activity ) { this.time = this.gpService.addMinutes( this.time, activity?.duration); } // 接收子组件传递的更新后时间 updateTime( updatedTime: Date ) { this.time = updatedTime; } }
Program组件模板
<div *ngFor="let subPrograma of programa;" class="mb-5"> {{ resetHour() }} <div class="my-3"> <div class="col-6 titulo_seccion ps-2"> {{ subProgram.sections[1].sectionName | uppercase }} </div> <!-- 向子组件传递活动数据与时间,并接收更新后的时间传递给下一个活动 --> <app-actividad *ngFor="let activity of subProgram.sections[1].activities" [activity]="activity" [time]="time" (timeChange)="updateTime($event)" ></app-actividad> </div> </div>
Actividad子组件
export class ActividadComponent implements OnInit{ @Input() activity !: Actividad; @Input() time !: Date; @Output() timeChange = new EventEmitter<Date>(); constructor( private gpService: ProgramGeneratorService ) { } // 在OnInit中为活动分配时间,并调用服务函数更新时间 ngOnInit(): void { this.activity.time = this.time; this.addMinutes(); } addMinutes(): void { this.time = this.gpService.addMinutes( this.time, this.activity.duration); this.timeChange.emit( this.time ); } }
Actividad子组件模板
<div class="row"> <div class="col-4"> <div class="row"> <div class="col text-start"> <span> {{ time | date: 'hh:mm' }} <span class="cr"> <span class="point">•</span> <!-- 此处为时间展示位置 --> {{ activity.activityName }} <span *ngIf="activity.time > 0" class="ms-1"> ({{ activity.time }} mins) </span> </span> </span> </div> </div> </div> </div>
ProgramGeneratorService服务
export class GeneradorProgramaService { constructor( private http: HttpClient ) { } // 时间更新函数 addMinutes( currentTime: Date, mins: number = 5 ): Date { return new Date( currentTime.getTime() + mins * 60000 ); } }
问题分析与修复方案
核心问题
- 子组件时间展示错误:子组件模板中显示的是修改后的结束时间,而非活动的开始时间,导致第一个活动直接显示结束时间。
- 组件初始化钩子不适用:
ngOnInit仅在组件初始化时执行一次,父组件更新time后,子组件无法响应输入变化,时间传递断裂。 - 模板中直接调用方法的副作用:
{{ resetHour() }}会在每次变更检测时执行,可能频繁重置时间,干扰正常累加逻辑。
修复步骤
1. 修正子组件时间展示逻辑
子组件应展示活动的开始时间(activity.time),同时修正时长判断的错误(activity.time是Date对象,不能直接与数字比较):
<div class="row"> <div class="col-4"> <div class="row"> <div class="col text-start"> <span> {{ activity.time | date: 'hh:mm' }} <span class="cr"> <span class="point">•</span> {{ activity.activityName }} <span *ngIf="activity.duration > 0" class="ms-1"> ({{ activity.duration }} mins) </span> </span> </span> </div> </div> </div> </div>
2. 改用ngOnChanges响应输入变化
替换ngOnInit为ngOnChanges,确保父组件更新time时,子组件能实时响应并更新活动时间:
import { Component, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core'; export class ActividadComponent implements OnChanges{ @Input() activity!: Actividad; @Input() time!: Date; @Output() timeChange = new EventEmitter<Date>(); constructor( private gpService: ProgramGeneratorService ) { } ngOnChanges(changes: SimpleChanges): void { // 仅当time输入发生变化时更新 if (changes['time']) { this.activity.time = this.time; const endTime = this.gpService.addMinutes(this.time, this.activity.duration); this.timeChange.emit(endTime); } } }
3. 修复父组件模板的时间重置逻辑
移除模板中直接调用resetHour()的写法,改为在组件逻辑中控制时间重置时机,避免变更检测的副作用:
<div *ngFor="let subPrograma of programa; trackBy: trackBySubProgram" class="mb-5"> <div class="my-3"> <div class="col-6 titulo_seccion ps-2"> {{ subPrograma.sections[1].sectionName | uppercase }} </div> <app-actividad *ngFor="let activity of subPrograma.sections[1].activities" [activity]="activity" [time]="time" (timeChange)="updateTime($event)" ></app-actividad> </div> </div>
在Program组件中添加trackBy优化循环,并在合适时机重置时间:
trackBySubProgram(index: number, subProgram: Program): any { return subProgram.id; // 用subProgram的唯一标识作为trackBy依据 } // 比如在获取program数据后初始化时间 ngOnInit(): void { // 假设此处从服务获取program数据 this.gpService.getProgram().subscribe(data => { this.program = data; this.startTime = this.program[0].startTime; this.resetHour(); }); }
4. 可选:预先在父组件计算所有活动时间
如果父子组件传递时间容易出问题,可以直接在父组件中预先计算每个活动的开始时间,子组件仅负责展示:
// Program组件中添加计算逻辑 calculateActivityTimes(subProgram: Program): void { let currentTime = new Date(this.startTime.getTime()); // 复制对象避免修改原数据 subProgram.sections[1].activities.forEach(activity => { activity.time = new Date(currentTime.getTime()); currentTime = this.gpService.addMinutes(currentTime, activity.duration); }); } ngOnInit(): void { this.gpService.getProgram().subscribe(data => { this.program = data; this.startTime = this.program[0].startTime; this.program.forEach(subProgram => this.calculateActivityTimes(subProgram)); }); }
修改父组件模板,不再传递时间相关属性:
<div *ngFor="let subPrograma of programa; trackBy: trackBySubProgram" class="mb-5"> <div class="my-3"> <div class="col-6 titulo_seccion ps-2"> {{ subPrograma.sections[1].sectionName | uppercase }} </div> <app-actividad *ngFor="let activity of subPrograma.sections[1].activities" [activity]="activity" ></app-actividad> </div> </div>
内容的提问来源于stack exchange,提问作者Leonardo Esquivel
相关产品推荐
相关产品推荐

