Angular Material日期选择器首次编译报date.clone is not a function错误
Angular Material Datepicker 首次编译报 date.clone is not a function 错误解决方案
问题现象
项目使用 Angular Material Datepicker 组件搭配 @angular/material-moment-adapter 适配库,配置日期值为Moment类型后,首次编译启动时抛出ERROR TypeError: date.clone is not a function at DatePickerAdapter.clone错误;不修改任何代码重新编译后错误自动消失,功能恢复正常。
相关代码
组件文件 mode.component.ts
import moment from 'moment'; import { FormControl } from '@angular/forms'; import { Component } from '@angular/core'; @Component({ selector: 'app-timesheet-day-mode', templateUrl: './mode.component.html' }) export class TimesheetDayModeComponent { chosenDate = new FormControl(moment()); }
模板文件 mode.component.html
<input matInput [formControl]="chosenDate" [matDatepicker]="picker" /> <mat-datepicker #picker></mat-datepicker>
模块配置文件 app.module.ts
import { NgModule } from '@angular/core'; import { DateAdapter, MatNativeDateModule } from '@angular/material/core'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatMomentDateModule, MomentDateAdapter } from "@angular/material-moment-adapter"; @NgModule({ imports: [ MatDatepickerModule, MatNativeDateModule, MatMomentDateModule, ], providers: [ { provide: DateAdapter, useClass: MomentDateAdapter }, ] }) export class AppModule { }
项目依赖版本
"dependencies": { "@agm/core": "^3.0.0-beta.0", "@angular-slider/ngx-slider": "^2.0.3", "@angular/animations": "^11.2.10", "@angular/cdk": "^11.0.0", "@angular/common": "^11.2.10", "@angular/compiler": "^11.2.10", "@angular/core": "^11.2.10", "@angular/flex-layout": "^11.0.0-beta.33", "@angular/forms": "^11.2.10", "@angular/material": "^11.0.0", "@angular/material-moment-adapter": "^11.0.0", "@angular/platform-browser": "^11.2.10", "@angular/platform-browser-dynamic": "^11.2.10", "@angular/router": "^11.2.10", "core-js": "^3.0.0", "file-saver": "^2.0.5", "hammerjs": "^2.0.8", "lodash": "^4.17.21", "moment": "^2.29.1", "moment-timezone": "^0.5.33", "ngx-bar-rating": "^2.0.0", "ngx-color": "^6.0.0", "ngx-image-cropper": "^4.0.1", "ngx-input-file": "^10.0.0", "ngx-mask": "^12.0.0", "ngx-toastr": "^13.0.0", "ngx-ui-switch": "^10.0.0", "numbro": "^2.3.6", "rxjs": "^6.6.7", "tslib": "^2.0.0", "uuid": "^8.3.2", "zone.js": "^0.11.0" }
问题根因
报错由日期适配器加载冲突+配置不规范共同导致:
- 同时引入了
MatNativeDateModule(适配原生JS Date对象)和MatMomentDateModule(适配Moment对象),两个模块都会向依赖注入容器注册自身的DateAdapter实现 - 首次冷编译时模块加载顺序不稳定,若原生适配器优先被注入,组件传入的Moment实例不存在原生Date对象的
clone方法,就会触发类型错误 - 无修改重编译时,热更新机制调整了模块加载优先级,Moment适配器优先生效,错误就会临时消失
- 手动重复声明
MomentDateAdapter提供者、moment默认导入方式的兼容性问题,也会提升这类偶发报错的概率 - 另外当前依赖中
@angular/material、@angular/cdk、@angular/material-moment-adapter版本为11.0.0,和Angular核心库的11.2.10存在小版本差异,也可能引发编译时的兼容性问题
修复步骤
- 从模块imports数组中移除
MatNativeDateModule,两类日期适配器模块不可同时导入 - 删除providers中手动声明的
MomentDateAdapter配置,MatMomentDateModule导入后已经自动完成了适配器的注册,重复声明反而会干扰加载顺序 - 调整moment的导入方式为命名空间导入,避免默认导入带来的实例初始化时序问题:将
import moment from 'moment'修改为import * as moment from 'moment' - (可选)将
@angular/material、@angular/cdk、@angular/material-moment-adapter三个包的版本对齐到11.2.x,和Angular核心库版本保持一致,消除小版本兼容隐患
修改后的核心模块配置参考:
import { NgModule } from '@angular/core'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatMomentDateModule } from "@angular/material-moment-adapter"; @NgModule({ imports: [ MatDatepickerModule, MatMomentDateModule, ] }) export class AppModule { }
如果修改后仍存在偶发报错,可以在组件构造函数中强制指定适配器配置,彻底避免加载时序问题:
import { Component } from '@angular/core'; import { FormControl } from '@angular/forms'; import * as moment from 'moment'; import { DateAdapter } from '@angular/material/core'; @Component({ selector: 'app-timesheet-day-mode', templateUrl: './mode.component.html' }) export class TimesheetDayModeComponent { chosenDate = new FormControl(moment()); constructor(private dateAdapter: DateAdapter<moment.Moment>) { // 可替换为项目实际使用的语言区域 this.dateAdapter.setLocale('zh-CN'); } }
内容的提问来源于stack exchange,提问作者林敬軒
相关产品推荐
相关产品推荐

