自定义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更新失败。
修复步骤:
- 修正
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; }
- 动态适配语言对应的日期格式
如果需要根据语言切换格式,可在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); }
- 确保
DatePipe正确提供
在AppModule中显式声明DatePipe提供者:
import { DatePipe } from '@angular/common'; @NgModule({ ... providers: [ DatePipe, {provide: NgbDateParserFormatter, useClass: NgbDateCustomParserFormatter} ] }) export class AppModule {}
内容的提问来源于stack exchange,提问作者Christian Del Rio
相关产品推荐
相关产品推荐

