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

自定义NgbDateParserFormatter导致ngb-datepicker模型值不更新

问题:自定义NgbDateParserFormatter后Angular日期选择器模型不更新

我实现了一个包含Angular日期选择器的自定义组件,为了根据应用所选语言格式化日期显示,编写了自定义NgbDateParserFormatter。添加该格式化器后,日期显示正常,但父组件获取值时模型无法更新;移除该提供者后,模型值更新恢复正常。


自定义日期选择器组件代码

custom-datepicker.html

<div>
  <input
    ngbDatepicker
    class="form-control"
    [(ngModel)]="date"
    #dateInput="ngbDatepicker"
    (click)="onToggle(dateInput)"
    (ngModelChange)="onChangeDate($event)"
    ngControl="date" name="dateInput"/>
  <button class="btn input-group-text date-picker-toggle-btn" (click)="dateInput.toggle()" type="button"></button>
</div>

custom-datepicker.ts

import { Component, EventEmitter, Input, Output } from '@angular/core';
import {
  NgbCalendar,
  NgbDateStruct,
  NgbDatepickerI18n,
} from '@ng-bootstrap/ng-bootstrap';

@Component({
  selector: 'date-picker',
  templateUrl: './date-picker.component.html',
  styleUrls: ['./date-picker.component.scss'],
  providers: [],
})
export class DatePickerComponent {
  @Input('date')
  date: NgbDateStruct;
  @Output()
  dateChange: EventEmitter<any> = new EventEmitter<any>();
  today = this.calendar.getToday();

  constructor(private calendar: NgbCalendar) {
  }

  onChangeDate(newDate: any) {
    this.dateChange.emit(newDate);
  }

  onToggle(dateInput) {
    dateInput.toggle();
  }
}

自定义格式化器代码

ngbDateCustomParserFormatter.ts

import { NgbDateParserFormatter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { DatePipe } from '@angular/common';

function toInteger(value: any): number {
  return parseInt(`${value}`, 10);
}

function isNumber(value: any): value is number {
  return !isNaN(toInteger(value));
}

@Injectable()
export class NgbDateCustomParserFormatter extends NgbDateParserFormatter {
  constructor(private datePipe: DatePipe, private translator: TranslateService) {
    super();
  }

  parse(value: string): NgbDateStruct {
    if (value) {
      const dateParts = value.trim().split('-');
      if (dateParts.length === 1 && isNumber(dateParts[0])) {
        return {day: toInteger(dateParts[0]), month: null, year: null};
      } else if (dateParts.length === 2 && isNumber(dateParts[0]) && isNumber(dateParts[1])) {
        return {day: toInteger(dateParts[0]), month: toInteger(dateParts[1]), year: null};
      } else if (dateParts.length === 3 && isNumber(dateParts[0]) && isNumber(dateParts[1]) && isNumber(dateParts[2])) {
        return {day: toInteger(dateParts[0]), month: toInteger(dateParts[1]), year: toInteger(dateParts[2])};
      }
    }
    return null;
  }

  format(date: NgbDateStruct): string {
    let dateObj = new Date(date.year, date.month - 1, date.day);
    return this.datePipe.transform(dateObj, 'dd/MM/yyyy');
  }
}

父组件使用方式

<date-picker [(date)]="expirationDate" id="expiration-date" />

(注:expirationDate为NgbDateStruct类型)


补充说明

已在AppModule和自定义日期选择器组件中添加该提供者,添加后模型无法更新,移除则正常:

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
  ],
  providers: [{provide: NgbDateParserFormatter, useClass: NgbDateCustomParserFormatter}]
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

解决方案

问题核心是自定义parse方法的解析规则与format方法的格式化规则不匹配:你的format方法将日期格式化为dd/MM/yyyy(斜杠分隔),但parse方法却按横杠-分割字符串解析,导致用户选择的日期无法被正确转换为NgbDateStruct,进而触发ngModel更新失败。

修复步骤:

  1. 修正parse方法的分隔符
    将parse方法中的分割符改为与format一致的斜杠/:
parse(value: string): NgbDateStruct {
  if (value) {
    // 改为按斜杠分割,匹配format方法的输出格式
    const dateParts = value.trim().split('/');
    if (dateParts.length === 1 && isNumber(dateParts[0])) {
      return {day: toInteger(dateParts[0]), month: null, year: null};
    } else if (dateParts.length === 2 && isNumber(dateParts[0]) && isNumber(dateParts[1])) {
      return {day: toInteger(dateParts[0]), month: toInteger(dateParts[1]), year: null};
    } else if (dateParts.length === 3 && isNumber(dateParts[0]) && isNumber(dateParts[1]) && isNumber(dateParts[2])) {
      // 对应dd/MM/yyyy的顺序:日、月、年
      return {day: toInteger(dateParts[0]), month: toInteger(dateParts[1]), year: toInteger(dateParts[2])};
    }
  }
  return null;
}
  1. 动态适配语言对应的日期格式
    如果需要根据语言切换格式,可在format方法中动态调整:
format(date: NgbDateStruct): string {
  if (!date) return '';
  const dateObj = new Date(date.year, date.month - 1, date.day);
  // 根据当前语言选择对应格式
  const format = this.translator.currentLang === 'en' ? 'MM/dd/yyyy' : 'dd/MM/yyyy';
  return this.datePipe.transform(dateObj, format);
}
  1. 确保DatePipe正确提供
    在AppModule中显式声明DatePipe提供者:
import { DatePipe } from '@angular/common';

@NgModule({
  ...
  providers: [
    DatePipe,
    {provide: NgbDateParserFormatter, useClass: NgbDateCustomParserFormatter}
  ]
})
export class AppModule {}

内容的提问来源于stack exchange,提问作者Christian Del Rio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 06:27:12