日期选择器允许用户通过文本输入或从日历中选择日期来输入日期。它由多个组件、指令和 日期实现 组成,它们协同工作。

日期选择器由一个文本输入框和一个日历弹出框组成,它们通过文本框上的 matDatepicker 联系起来。

还有一个可选的日期选择器切换按钮,它给了用户一种弹出日期选择器的简易方式。

当输入框作为 <mat-form-field> 的一部分时,也完全一样。 切换按钮可以很容易地作为输入框的前缀或后缀:

如果你要定制 mat-datepicker-toggle 中渲染的图标,可以使用 matDatepickerToggleIcon 指令:

如果你希望用户选择一个日期范围而不是单个日期,可以使用 mat-date-range-inputmat-date-range-picker 组件。它们可以关联使用,类似于 mat-date-range-inputmat-datepicker

mat-date-range-input 组件要求为开始日期和结束日期分别提供两个 input 元素:

<mat-date-range-input>
  <input matStartDate placeholder="Start date">
  <input matEndDate placeholder="End date">
</mat-date-range-input>

mat-date-range-picker 组件作为弹出式面板,用于选择日期。其工作方式和 mat-datepicker 相同,但允许用户多次选择:

<mat-date-range-picker #picker></mat-date-range-picker>

使用 rangePicker 属性把范围选择器和范围输入框联系起来:

<mat-date-range-input [rangePicker]="picker">
  <input matStartDate placeholder="Start date">
  <input matEndDate placeholder="End date">
</mat-date-range-input>

<mat-date-range-picker #picker></mat-date-range-picker>

mat-date-range-input 组件可以和来自 @angular/formsFormGroup 指令一起使用,它会把起始值和结束值组合在一起,并把它们作为一个组进行验证。

<mat-datepicker>startView 属性可用来指定当首次打开日历时,应该使用哪个视图。 它可以是 month(月)、year(年)或 multi-year(多年)之一,默认情况下是月。

此日历中打开的月份、年份或年份的范围,取决于当前是否选择了某个日期。如果选择了,它就会打开包含该日期的月份或年份。否则,它就打开包含当前日期的月份或年份。 这种行为可以用 <mat-datepicker>startAt 属性来改写。这种情况下,此日历将打开包含 startAt 日期的月份或年份。

如果在 multi-year / year 视图中选择年 / 月时,输出属性 yearSelected / monthSelected 将会发出一个表示所选年 / 月的标准化日期。 这里 "标准化" 的意思是:对于年,它会设置为当年的一月一日;对于月,它会设置为当月的第一天。例如: 如果 <mat-datepicker> 配置为和 JavaScript 的原生 Date 对象协同工作,则当用户在 multi-year 视图中选中 2017 年时,yearSelected 将发出 new Date(2017, 0, 1)。 同样,如果用户在 year 视图中选中了二月,则 monthSelected 将会发出 new Date(2017, 1, 1)。而当打开日历时相关 <input> 的当前日期值会设置为某个类似 new Date(2017, MM, dd) 的值(这种情况下月和日是无关的)。

注意,发出的值不会影响相关 <input> 的当前值,当前值只跟你在 month 视图中所做选的日期有关。 所以,如果最终用户在 multi-year 视图下选择了某一年后关闭了日历(比如通过按下 ESC 键),则由 yearSelected 发出的选定年份不会给相关 <input> 中的日期值带来任何变化。

下面的例子使用输出属性 yearSelectedmonthSelected 来模拟月和年的选择器(如果你对 MomentDateAdapterMAT_DATE_FORMATS 的用法还不熟,可以在稍后部分阅读它们以更好地理解该范例)。

Datepicker 的值类型取决于你提供的 DateAdapter 的类型。 比如,NativeDateAdapter 会直接使用普通的 JavaScript Date 对象;而使用 MomentDateAdapter 时,所有的值都会是 Moment.js 的实例。 这种适配器模式,可以让 Datepicker 组件借助自定义 DateAdapter 来处理日期的任何一种表示法。

根据所使用的 DateAdapter,日期选择器还可以自动重新序列化某些日期格式。例如:NativeDateAdapterMomentDateAdapter 都允许把 ISO 8601 字符串传给日期选择器, 并自动转换成合适的对象类型。 当直接把后端数据类型绑定到日期选择器时,这很方便。不过,日期选择器不接受用户格式的日期字符串(比如 "1/2/2017"), 因为它是有二义性的,它会根据执行代码的浏览器的时区设置不同而代表不同的日期。

<input> 的其它类型一样,日期选择器也能和 @angular/forms 中的指令协同工作,比如 formGroupformControlngModel 等。

当日期选择器弹出时,它会自动继承所附着的 mat-form-field 的调色板(primaryaccentwarn)。 如果你要为弹出框另行指定一个调色板,可以设置 mat-datepickercolor 属性。

有三个属性可以为日期选择器添加日期验证。前两个是 minmax 属性。除了对输入执行验证之外,这些属性还会禁用日期弹出框中相应值之前或之后的所有日期,并阻止用户将日历推进到包含 minmax 日期之外的 monthyear(取决于当前视图)。

添加验证器的第二种方式是使用日期选择器输入框的 matDatepickerFilter 属性。 该属性接受一个 <D> => boolean 型的函数(这里的 <D> 是日期选择器所用的日期类型,参见选择一个日期实现类)。 如果结果是 true 则表示该日期是有效的,如果为 false 则表示无效。同样,这也会禁用日历上那些无效的日期。 不过,matDatepickerFilterminmax 之间有一个重要的差异 —— 如果过滤掉了特定时间点之前或之后的所有日期,并不会阻止用户把日历推进到无效的日期范围内。

在此示例中,用户无法选择任何落在星期六或星期日的日期,但所有落在其他工作日的日期都是可选择的。

每个验证属性可以检查出不同的错误:

输入框原生的 (input)(change) 事件只会因为用户和输入框元素的交互而触发;当用户在日历弹出框中选择日期时则不会触发。 因此,日期选择框的输入还支持 (dateInput)(dateChange) 事件。无论用户输入还是在弹出框中选择都会触发这两个事件。

每当用户正在输入或正在日历中点选日期时都会触发 (dateInput) 事件。 而当用户结束了输入(<input> 失焦)或在日历中选好了日期时,就会触发 (dateChange) 事件。

像任何标准的 <input> 一样,也可以通过添加 disabled 属性来禁用日期选择器的输入框。 默认情况下,<mat-datepicker><mat-datepicker-toggle> 将会从 <input> 中继承禁用状态,不过也可以通过设置日期选择器或开关元素的 disabled 属性来覆盖它。 如果你想禁用文本框却允许通过日历进行选取(或反之)时,这会很有用。

默认情况下,单击日历中的某个日期会选择它并关闭日历弹出窗口。在某些情况下,这可能是不可取的,因为如果用户改变主意,用户就无法快速返回。如果你希望你的用户可以取消他们的选择,而必须明确接受他们已经选择的值,你可以在 <mat-datepicker> 中添加一个 <mat-datepicker-actions> 元素,内含带有 matDatepickerCancel 属性的 “Cancel” 按钮和带有 matDatepickerApply 属性的 “Apply” 按钮。这样就会让日期选择器只有在用户按下 “Apply” 的情况下才把值赋给数据模型,而按下 “Cancel” 则会关闭弹出窗口而不改变值。

此 actions 元素也支持 <mat-date-range-picker>,它叫做 <mat-date-range-picker-actions>,其按钮分别叫做 matDateRangePickerCancelmatDateRangePickerApply

如果你的用户需要把他们当前正在选择的日期范围与另一个范围进行比较,你可以使用 comparisonStartcomparisonEnd 绑定来为 mat-date-range-input 提供比较范围。比较范围会在日历中静态渲染,但会改变颜色,以便指示哪些日期与用户选定的范围重叠。

请注意,由于 Material Design 主题体系的限制,比较色和重叠色并非来自当前主题。可以使用 datepicker-date-range-colors mixin 来定制它们。

@use '@angular/material' as mat;

@include mat.datepicker-date-range-colors(
  hotpink, teal, yellow, purple);

mat-date-range-picker 支持自定义范围预览和选择的行为。为了自定义它,首先要创建一个实现 MatDateRangeSelectionStrategy 的类,然后通过 MAT_DATE_RANGE_SELECTION_STRATEGY 令牌提供该类。下面的例子使用这种范围选择策略来创建一个自定义范围选择器,它可以把用户限制在五天的范围内。

正常情况下,日期选择器会在输入框下方打开一个弹出框。不过,这对于触屏设备很不理想,它们屏幕较小,并且需要更大的点击目标。 出于这个原因,<mat-datepicker> 有一个 touchUi 属性,它可以设置为 true 以便在大对话框中打开日历时启用更加友好的 UI。

日历弹出框可以使用 <mat-datepicker>openclose 方法进行程序化控制。 它还有一个 opened 属性来反应弹出框的状态。

如果要让用户从页面内联日历而不是从包含在弹出窗口中的日历中选择日期,可以直接使用 <mat-calendar>。日历的高度是根据一个月需要显示的宽度和日期数自动确定的。如果你想让日历更大或更小,请调整宽度而不是高度。

日期选择器的国际化配置包括四个方面:

  1. 日期语言环境。
  2. 日期选择器所能接受的日期对象实现。
  3. 日期选择器所用的显示和解析格式。
  4. 在日期选择器 UI 中使用的各种消息字符串。

默认情况下,依赖注入令牌 MAT_DATE_LOCALE 将会使用来自 @angular/core 中的 LOCALE_ID 表示的区域代码。 如果你要覆盖它,可以为 MAT_DATE_LOCALE 令牌提供一个新值:

bootstapApplication(MyApp, {
  providers: [{provide: MAT_DATE_LOCALE, useValue: 'en-GB'}],
});

也可以使用 DateAdaptersetLocale 方法在运行时设置语言环境。

注意:如果你使用的是 provideDateFnsAdapter,除了向 MAT_DATE_FORMATS 提供与 date-fns 兼容的配置外,你还需要将你的语言环境的数据对象提供给 MAT_DATE_LOCALE,而不是语言环境代码。 date-fns 的语言环境数据可以从 date-fns/locale 导入。

日期选择器是和日期的具体实现无关的,也就是说它可以和多种不同的日期协同工作。不过,这也意味着开发者要确保为日期选择器提供恰当的实现来支持所选的日期实现。

确保这一点最简单的方法是导入提供的日期适配器之一:

provideNativeDateAdapterMatNativeDateModule

日期类型 Date
支持的语言环境 en-US
依赖项
导入自 @angular/material/core

provideDateFnsAdapterMatDateFnsModule(通过 ng add @angular/material-date-fns-adapter 安装)

日期类型 Date
支持的语言环境 查看项目以获取详细信息
依赖项 date-fns
导入自 @angular/material-date-fns-adapter

provideLuxonDateAdapterMatLuxonDateModule(通过 ng add @angular/material-luxon-adapter 安装)

日期类型 DateTime
支持的语言环境 查看项目以获取详细信息
依赖项 Luxon
导入自 @angular/material-luxon-adapter

provideMomentDateAdapterMatMomentDateModule(通过 ng add @angular/material-moment-adapter 安装)

日期类型 Moment
支持的语言环境 查看项目以获取详细信息
依赖项 Moment.js
导入自 @angular/material-moment-adapter

请注意: provideNativeDateAdapter 基于 JavaScript 原生 Date 对象 中提供的功能。因此,它不适合许多语言环境。原生 Date 对象最大的缺点之一是无法设置解析格式。我们强烈建议使用基于更强大的格式化和解析库的适配器。你可以使用 provideMomentDateAdapter 或自定义 DateAdapter,它可以与你选择的库一起使用。

这些 API 包含 DateAdapterMAT_DATE_FORMATS 的提供者。

bootstrapApplication(MyApp, {
  providers: [provideNativeDateAdapter()]
});

因为 DateAdapter 是一个泛型类,所以 MatDatepickerMatDatepickerInput 也要做成泛型的。 当使用这些类时(比如作为 ViewChild),你要包含与所用的 DateAdapter 实现相对应的泛型类。 比如:

@Component({...})
export class MyComponent {
  @ViewChild(MatDatepicker) datepicker: MatDatepicker<Date>;
}

默认情况下, MomentDateAdapter 会在你的时区特定语言环境中创建日期。你可以将默认行为更改为将日期解析为 UTC,方法是将 useUtc: true 传递给 provideMomentDateAdapter 或提供 MAT_MOMENT_DATE_ADAPTER_OPTIONS 注入令牌。

bootstrapApplication(MyApp, {
  providers: [provideMomentDateAdapter(undefined, {useUtc: true})]
});

默认情况下, MomentDateAdapter 将以 宽松的方式 解析日期。这可能会导致日期解析错误。你可以将默认行为更改为 严格解析日期,方法是将 strict: true 传递给 provideMomentDateAdapter 或提供 MAT_MOMENT_DATE_ADAPTER_OPTIONS 注入令牌。

bootstrapApplication(MyApp, {
  providers: [provideMomentDateAdapter(undefined, {strict: true})]
});

你还可以创建和应用所需的任何日期格式协同工作的自定义 DateAdapter。这可以通过创建 DateAdapter 的子类并把它作为 DateAdapter 的实现来完成。 你还要确保应用中所提供的 MAT_DATE_FORMATS 都是你的日期实现所能理解的格式。要了解关于 MAT_DATE_FORMATS 的更多信息,参见自定义解析和显示格式

bootstrapApplication(MyApp, {
  providers: [
    {provide: DateAdapter, useClass: MyDateAdapter},
    {provide: MAT_DATE_FORMATS, useValue: MY_DATE_FORMATS},
  ]
});

如果你需要使用原生 Date 对象,但需要自定义行为(例如自定义日期解析),你可以考虑子类 NativeDateAdapter

MAT_DATE_FORMATS 对象是一组日期选择器在解析和显示日期时所能使用的格式。这些格式都会传给 DateAdapter,所以你要确保这些格式对象能和应用中所用的 DateAdapter 兼容。

如果你想使用 Angular Material 附带的 DateAdapters 之一,但使用你自己的 MAT_DATE_FORMATS,你可以将格式传递给提供者函数,或者自己提供 MAT_DATE_FORMATS 令牌。例如:

bootstrapApplication(MyApp, {
  providers: [provideNativeDateAdapter(MY_NATIVE_DATE_FORMATS)],
});

要使用自定义格式与 provideMomentDateAdapter 一起使用,你可以从 这里 文档中提供的解析格式和 这里 文档中提供的显示格式中进行选择。

也可以支持多种解析格式。例如:

bootstraApplication(MyApp, {
  providers: [provideMomentDateAdapter({
    parse: {
      dateInput: ['l', 'LL'],
    },
    display: {
      dateInput: 'L',
      monthYearLabel: 'MMM YYYY',
      dateA11yLabel: 'LL',
      monthYearA11yLabel: 'MMMM YYYY',
    },
  })]
});

你可以用自定义组件代替日历的头部(也就是包含视图切换器、向前和向后按钮的地方)。 这可以通过 <mat-datepicker>calendarHeaderComponent 属性来实现。 它接受一个组件类,并构建一个该组件的实例,将其用作日历头。

要想在自定义的日历头组件中和日历互动,你可以在它的构造函数中注入父 MatCalendar。 要确保你的日历头与日历保持同步,请订阅此日历的 stateChanges 事件,并把你的日历头组件标记为已更改的。

日期选择器使用的各种文本字符串通过 MatDatepickerIntl 提供。可以通过在你的应用配置中提供一个带有翻译值的子类来实现这些消息的本地化。

bootstrapApplication(MyApp, {
  providers: [
    {provide: MatDatepickerIntl, useClass: MyIntl},
    provideNativeDateAdapter(),
  ],
});

如果你想把一个或多个 CSS 类应用到日历中的某些日期(比如突出显示一个假日),你可以使用输入属性 dateClass。它接受一个函数,将对日历中的每个日期调用该函数,并应用其返回的任何类。返回值可以是 ngClass 接受的任何值。

MatDatepicker 弹出窗口使用 role="dialog" 交互模式。该对话框包含多个控件,最突出的是日历本身。这个日历实现了 role="grid" 交互模式。

总是启用确认操作按钮。这允许使用辅助技术的用户在提交值之前明确确认他们的选择。

MatDatepickerInputMatDatepickerToggle 会给原生输入框和开关按钮分别加上 aria-haspopup 属性,而它们触发的日历弹出框则会带有 role="dialog" 属性。

MatDatepickerIntl 包含一些要用作 aria-label 的字符串。日期选择器的输入框应该具有一个占位符或通过 aria-labelaria-labelledbyMatDatepickerIntl 提供一个有意义的标签。

总是传达日期格式(例如“MM/DD/YYYY”)。这可以使用 <mat-hint> 或通过在表单字段附近提供额外标签来完成。

MatDatepickerInput 会添加 Alt + Down Arrow 作为键盘快捷键以打开日期选择器弹出窗口。但是,ChromeOS 在操作系统层面拦截了这个组合键,使得浏览器只能接收一个 PageDown 键盘事件。由于这种行为,你应该总是提供其他打开弹出窗口的方法,例如 MatDatepickerToggle

日期选择器支持下列键盘快捷键:

键盘快捷键 操作
Alt + 下箭头 打开日历弹出窗口
Esc 关闭日历弹出窗口

在月份视图中:

快捷键 操作
左箭头 转到前一天
右箭头 转到后一天
上箭头 转到前一周的同一天
下箭头 转到后一周的同一天
Home 转到本月的第一天
End 转到本月的最后一天
Page Up 转到前一个月的同一天
Alt + Page Up 转到前一年的同一天
Page Down 转到后一个月的同一天
Alt + Page Down 转到后一年的同一天
回车 选择当前日期

在年份视图中:

快捷键 操作
左箭头 转到前一个月
右箭头 转到下个月
上箭头 向上移动一行(后退 4 个月)
下箭头 向下移动一行(前进 4 个月)
Home 转到今年的第一个月
End 转到今年的最后一个月
Page Up 转到前一年的同一个月
Alt + Page Up 转到 10 年前的同一个月
Page Down 转到下一年的同一个月
Alt + Page Down 转到 10 年后的同一个月
回车 选择当前月份

在多年视图中:

快捷键 操作
左箭头 转到前一年
右箭头 转到下一年
上箭头 向上移动一行(后退 4 年)
下箭头 向下移动一行(前进 4 年)
Home 转到当前范围内的第一年
End 转到当前范围内的最后一年
Page Up 后退 24 年
Alt + Page Up 后退 240 年
Page Down 前进 24 年
Alt + Page Down 前进 240 年
回车 选择当前年份

如果你没有提供日期选择器工作所需的所有可注入对象,就会抛出此错误。 解决此问题的最简单方法是在你的应用配置中添加 provideNativeDateAdapterprovideMomentDateAdapter。 有关更多信息,请参阅 选择日期实现

如果多个 <input> 视图(通过 matDatepicker 属性)获取同一个 <mat-datepicker> 的所有权,就会抛出本错误。 一个日期选择器只能和一个输入框相关联。

如果没有给 <mat-datepicker> 关联任何 <input>,就会发生本错误。 要想把一个输入框和日期选择器关联起来,请创建一个到该日期选择器的模板引用,并把它赋值给输入框上的 matDatepicker 属性:

<input [matDatepicker]="picker">
<mat-datepicker #picker></mat-datepicker>