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

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 );
  }
}

问题分析与修复方案

核心问题

  1. 子组件时间展示错误:子组件模板中显示的是修改后的结束时间,而非活动的开始时间,导致第一个活动直接显示结束时间。
  2. 组件初始化钩子不适用:ngOnInit仅在组件初始化时执行一次,父组件更新time后,子组件无法响应输入变化,时间传递断裂。
  3. 模板中直接调用方法的副作用:{{ 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 11:45:36