Angular Material Timepicker 组件完全指南:API 结构、表单集成与下拉选项定制
【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/components
Angular Material Timepicker 是官方组件库(@angular/material/timepicker)中用于设置日期对象"时间部分"的组件:用户既可以直接在输入框中键入时间,也可以从预定义的下拉选项列表中选取。本文以当前仓库的公开 API 报告(goldens/material/timepicker/index.api.md)为骨架,结合官方指南(src/material/timepicker/timepicker.md)与源码实现(timepicker.ts、timepicker-input.ts、util.ts),系统讲解 Timepicker 的 API 结构、@angular/forms集成、与mat-datepicker组合成日期时间选择器、下拉选项定制、国际化与无障碍配置,帮助你直接落地到实际业务表单中。
一、组件 API 总览:从公开 API 报告认识 Timepicker
goldens/material/timepicker/index.api.md是由 API Extractor 自动生成的公开 API 报告,它精确刻画了@angular/material_timepicker包的对外接口。整包对外导出 3 个组件/指令与 4 个接口、2 个注入令牌:
| 导出符号 | 类型 | 作用 |
|---|---|---|
MatTimepicker<D> | 组件(selector:mat-timepicker) | 渲染时间下拉面板,管理打开/关闭与选项生成 |
MatTimepickerInput<D> | 指令(selector:input[matTimepicker]) | 时间输入框,同时是ControlValueAccessor与Validator |
MatTimepickerToggle<D> | 组件(selector:mat-timepicker-toggle) | 用于打开面板的时钟按钮 |
MatTimepickerModule | NgModule | 导出以上三者及CdkScrollableModule |
MatTimepickerConfig | 接口 | 全局默认配置(interval、disableRipple) |
MatTimepickerConnectedInput<D> | 接口 | Timepicker 与输入框之间的连接契约 |
MatTimepickerOption<D> | 接口 | 自定义下拉选项结构(label+value) |
MatTimepickerSelected<D> | 接口 | 选中事件载荷(value+source) |
MAT_TIMEPICKER_CONFIG | 注入令牌 | 覆盖全局默认配置 |
MAT_TIMEPICKER_SCROLL_STRATEGY | 注入令牌 | 自定义面板滚动策略 |
从ɵcmp/ɵdir声明可以确认各成员的信号化输入输出关系,例如MatTimepicker的输入有interval、options、disableRipple、aria-label、aria-labelledby、panelClass,输出为selected、opened、closed;MatTimepickerInput的输入为value、matTimepicker(必选)、matTimepickerMin、matTimepickerMax、matTimepickerOpenOnClick、disabled,输出为valueChange。这些映射与源码中input(...)/model(...)/output()的声明一一对应。
1.1MatTimepickerConfig与MAT_TIMEPICKER_CONFIG
export interface MatTimepickerConfig { /** Default interval for all time pickers. */ interval?: string | number; /** Whether ripples inside the timepicker should be disabled by default. */ disableRipple?: boolean; }该令牌定义在 util.ts,MatTimepicker与MatTimepickerToggle都会以可选注入的方式读取它(inject(MAT_TIMEPICKER_CONFIG, {optional: true})),用于初始化interval与disableRipple的默认值。
1.2MatTimepickerConnectedInput:面板与输入框的桥梁
export interface MatTimepickerConnectedInput<D> { value: Signal<D | null>; min: Signal<D | null>; max: Signal<D | null>; disabled: Signal<boolean>; focus(): void; getOverlayOrigin(): ElementRef<HTMLElement>; getLabelId(): string | null; timepickerValueAssigned(value: D | null): void; }MatTimepickerInput实现该接口并在构造时通过registerInput()注册到 Timepicker(见 timepicker-input.ts)。Timepicker 打开面板时依赖min/max生成选项、依赖getOverlayOrigin()定位浮层、选中后调用timepickerValueAssigned()回写值。
二、最小可运行配置:连接输入框与开关按钮
一个 Timepicker 由"文本输入框 + 下拉面板"组成,二者通过输入框上的matTimepicker绑定关联;mat-timepicker-toggle是可选的开关按钮,便于用户一键展开面板。官方最小示例(见 timepicker-overview-example.html):
<mat-form-field> <mat-label>Pick a time</mat-label> <input matInput [matTimepicker]="picker"> <mat-timepicker-toggle matIconSuffix [for]="picker"/> <mat-timepicker #picker/> </mat-form-field>mat-timepicker组件本身没有任何可视化内容,它只是一个"面板逻辑载体",最终通过TemplatePortal挂载到由 CDK Overlay 创建的浮层上(见 timepicker.ts 的open()流程)。mat-timepicker-toggle通过必选输入for(对应timepicker输入信号)指向 Timepicker 实例。- 输入框与开关既可以独立使用,也可以像上面这样放进
mat-form-field,此时输入框的getOverlayOrigin()会优先返回表单字段的连接原点(见 timepicker-input.ts),面板宽度会跟随输入框宽度(overlayRef.updateSize({width: input.getOverlayOrigin().nativeElement.offsetWidth}))。
使用模块时只需引入MatTimepickerModule(或 Standalone 模式下直接 importMatTimepicker、MatTimepickerInput、MatTimepickerToggle),该模块同时导出CdkScrollableModule以支持面板内滚动(见 timepicker-module.ts)。
三、表单集成:Timepicker 是原生的表单控件
MatTimepickerInput通过自身提供的NG_VALUE_ACCESSOR、NG_VALIDATORS与MAT_INPUT_VALUE_ACCESSOR三个 provider 接入 Angular 表单体系(见 timepicker-input.ts),这意味着它可以直接用于formControl、ngModel或响应式表单:
<mat-form-field> <mat-label>Pick a time</mat-label> <input matInput [formControl]="formControl" [matTimepicker]="picker"> <mat-timepicker-toggle matIconSuffix [for]="picker"/> <mat-timepicker #picker/> </mat-form-field>完整示例见 timepicker-forms-example.html。
关键行为(来自官方指南 "Timepicker forms integration" 一节):
- 用户键入新时间或从下拉中选择时间后,该时间会被设置到当前表单控件持有的日期对象上,即只修改日期对象的时分秒部分。
- 如果表单控件当前没有值,Timepicker 会以今天的日期 + 所选时间创建一个新日期对象。
ControlValueAccessor接口的writeValue在写入时会先经deserialize再getValidDateOrNull清洗,避免无效值污染模型(见 timepicker-input.ts)。- 当用户输入非法时间后恢复合法输入时,组件会用
_lastValidDate保存的上一个合法日期并覆盖其时间,从而不丢失日期部分(见_assignUserSelection,timepicker-input.ts)。
3.1 与MatDatepicker组合:实现"日期 + 时间"选择器
Material 的 datepicker 与 timepicker 可以作用于同一个值:datepicker 设置整个日期对象,timepicker 只修改其中的时间部分,二者互补即可组合出完整的 datetime 选择器。官方示例(timepicker-datepicker-integration-example.html):
<mat-form-field> <mat-label>Meeting date</mat-label> <input matInput [matDatepicker]="datepicker" [(ngModel)]="value"> <mat-datepicker #datepicker/> <mat-datepicker-toggle [for]="datepicker" matSuffix/> </mat-form-field> <mat-form-field> <mat-label>Meeting time</mat-label> <input matInput [matTimepicker]="timepicker" [(ngModel)]="value" [ngModelOptions]="{updateOn: 'blur'}"> <mat-timepicker #timepicker/> <mat-timepicker-toggle [for]="timepicker" matSuffix/> </mat-form-field>两个组件共享同一个value绑定。由于 timepicker 内部依赖DateAdapter.sameTime判断值是否变化,与 datepicker 共用同一套日期适配器即可保证时间部分的读写一致。示例中为时间输入框设置了updateOn: 'blur',避免键入过程中频繁写值。
四、输入校验:解析错误与上下界约束
官方指南的 "Input validation" 一节明确了 Timepicker 输入框的两类校验职责:时间字符串是否合法、是否落在matTimepickerMin/matTimepickerMax边界内。
4.1 解析错误matTimepickerParse
当用户键入非法时间字符串(如abc、24:67)时,输入框上报matTimepickerParse错误。字符串由当前日期实现的parseTime方法解析——即底层DateAdapter.parseTime(value, MAT_DATE_FORMATS.parse.timeInput)。以原生适配器为例,其TIME_REGEX只接受如下形态(见 native-date-adapter.ts):
H:mm、H:mm:ssH:mm AM/PM、H:mm:ss AM/PM- 分隔符
:或.(即也接受22.45这种写法)
4.2 上下界错误matTimepickerMin/matTimepickerMax
matTimepickerMin与matTimepickerMax输入(别名对应源码中的min/max信号)接受带具体时间的日期对象或时间字符串,二者同时决定:
- 用户可输入的时间边界;
- 下拉面板内实际渲染的选项范围。
示例(官方指南原例):
<input matInput [formControl]="formControl" [matTimepicker]="picker" matTimepickerMin="12:30" matTimepickerMax="17:30">设置matTimepickerMin="12:30"与matTimepickerMax="21:25"后,用户只能在下午 12:30 至晚上 9:25 之间选择;越界值会通过值访问器分别上报matTimepickerMin/matTimepickerMax错误。错误对象的结构(来自 timepicker-input.ts 的验证器组合):
matTimepickerParse:{text: 用户输入文本}matTimepickerMin:{min, actual}(actual为反序列化后的当前值)matTimepickerMax:{max, actual}
在模板中可用@if配合mat-error展示错误信息(完整示例见 timepicker-validation-example.html):
@if (formControl.errors?.['matTimepickerParse']) { <mat-error>Value isn't a valid time</mat-error> } @if (formControl.errors?.['matTimepickerMin']) { <mat-error>Value is too early</mat-error> } @if (formControl.errors?.['matTimepickerMax']) { <mat-error>Value is too late</mat-error> }实现上,_updateFormsState()中的 effect 持续计算valueValid、_minValid、_maxValid三个状态,任一状态变化即触发_validatorOnChange()(见 timepicker-input.ts),验证器使用Validators.compose合并三个校验函数。min/max的字符串输入经_transformDateInput调用parseTime转为日期对象,解析失败则返回null(视为不设界)。
五、自定义下拉选项:interval 与 options
默认情况下,mat-timepicker下拉面板以30 分钟为间隔生成选项。可通过两种方式定制:interval输入或options输入(二者互斥)。
5.1 使用interval输入
<mat-timepicker interval="90m"/>interval接受字符串或数字,合法的间隔写法(官方指南完整列举):
| 写法 | 含义 | 说明 |
|---|---|---|
50 | 50 分钟 | 纯数字按分钟解释 |
30m/5h | 30 分钟 / 5 小时 | 短单位:h/H小时,m/M分钟,s/S秒 |
75 min/1.5 hours | 75 分钟 / 1.5 小时 | 长单位:min/minute/minutes、hour/hours、second/seconds |
底层解析由 util.ts 的parseInterval完成,其匹配正则与换算规则如下:
const INTERVAL_PATTERN = /^(\d*\.?\d+)\s*(h|hour|hours|m|min|minute|minutes|s|second|seconds)?$/i;- 小时单位 × 3600、分钟单位 × 60、其余按秒处理,最终统一换算为秒;
- 无法匹配或 NaN 时返回
null(回退到默认 30 分钟)。
选项的生成逻辑在_generateOptions()(timepicker.ts):默认间隔为30 * 60秒;起始时间为min(缺省为当天 00:00:00),结束时间为max(缺省为当天 23:59:00);生成结果会以interval/格式化min/格式化max作为缓存键,避免输入未变化时重复计算。generateOptions中还设置了Math.max(interval, 1)的下限,防止亚秒间隔导致浏览器卡死(见 util.ts)。
5.2 通过MAT_TIMEPICKER_CONFIG设置全局默认间隔
若想让应用内所有 Timepicker 默认使用某个间隔,可在providers中提供MAT_TIMEPICKER_CONFIG(官方指南原例):
import {MAT_TIMEPICKER_CONFIG} from '@angular/material/timepicker'; { provide: MAT_TIMEPICKER_CONFIG, useValue: {interval: '90 minutes'}, }5.3 使用options输入提供完全自定义的选项
当需要更细粒度的控制(比如按业务时间段分组)时,可传入符合MatTimepickerOption接口的数组:
export interface MatTimepickerOption<D = unknown> { value: D; // 选项的日期值 label: string; // 展示给用户的文本 }官方示例(timepicker-options-example.ts):
customOptions: MatTimepickerOption<Date>[] = [ {label: 'Morning', value: new Date(2024, 0, 1, 9, 0, 0)}, {label: 'Noon', value: new Date(2024, 0, 1, 12, 0, 0)}, {label: 'Evening', value: new Date(2024, 0, 1, 22, 0, 0)}, ];<mat-timepicker [options]="customOptions" #customPicker/>对应的 HTML 模板中还演示了interval="45min"、interval="3.5h"两种间隔写法(见 timepicker-options-example.html)。注意:options与interval不能同时指定,options也不能是空数组——违反这两条规则会在开发模式下直接抛出异常(见 timepicker.ts 的 effect 校验)。
六、自定义开关图标
mat-timepicker-toggle默认渲染一个时钟图标。通过matTimepickerToggleIcon属性将自定义元素投影进按钮内部即可替换(见 timepicker-toggle.html 的ng-content投影逻辑与官方示例 timepicker-custom-icon-example.html):
<mat-timepicker-toggle matIconSuffix [for]="picker"> <mat-icon matTimepickerToggleIcon>keyboard_arrow_down</mat-icon> </mat-timepicker-toggle>MatTimepickerToggle其余可用输入(来自 API 报告):for(必选)、aria-label、aria-labelledby、disabled、tabIndex、disableRipple。默认 ARIA 标签为 "Open timepicker options"(见 timepicker-toggle.ts)。值得注意的实现细节:toggle 的点击事件绑定在宿主元素上并在打开面板后调用event.stopPropagation(),以避免表单字段自动聚焦到输入框(见 timepicker-toggle.ts)。
七、国际化:语言、日期实现与显示格式
Timepicker 的国际化与mat-datepicker共享同一套DateAdapter机制,由三个要素共同决定:
- 日期 locale(
MAT_DATE_LOCALE); - 日期实现(Native / date-fns / Luxon / Moment 适配器);
- 显示与解析格式(
MAT_DATE_FORMATS)。
7.1 设置 locale
默认情况下MAT_DATE_LOCALE使用@angular/core的LOCALE_ID。要覆盖它,可在启动配置中提供新值:
bootstrapApplication(MyApp, { providers: [{provide: MAT_DATE_LOCALE, useValue: 'en-GB'}], });也可以在运行时通过DateAdapter.setLocale()动态切换。官方示例即用一个按钮在运行期把 locale 切到保加利亚语(timepicker-locale-example.ts):
this._adapter.setLocale('bg-BG');注意:若使用provideDateFnsAdapter,MAT_DATE_LOCALE需要提供该 locale 的数据对象(从date-fns/locale导入)而非 locale 代码,同时还需向MAT_DATE_FORMATS提供与date-fns兼容的配置。
7.2 选择日期实现
Timepicker 与实现无关(implementation-agnostic),官方推荐直接使用随包提供的适配器之一:
| 适配器(函数式 / 模块式) | 日期类型 | 依赖 | 引入位置 |
|---|---|---|---|
provideNativeDateAdapter/MatNativeDateModule | Date | 无 | @angular/material/core |
provideDateFnsAdapter/MatDateFnsModule | Date | date-fns | @angular/material-date-fns-adapter(ng add @angular/material-date-fns-adapter) |
provideLuxonDateAdapter/MatLuxonDateModule | DateTime | Luxon | @angular/material-luxon-adapter |
provideMomentDateAdapter/MatMomentDateModule | Moment | Moment.js | @angular/material-moment-adapter |
例如使用 date-fns 适配器:
import {provideDateFnsAdapter} from '@angular/material-date-fns-adapter'; bootstrapApplication(MyApp, { providers: [provideDateFnsAdapter()] });原生适配器的时间解析限制:provideNativeDateAdapter通过正则实现时间解析,只支持 AM/PM 时间(如1:45 PM)或 24 小时制时间(如22:45、22.45),无法适配格式不同的 locale。若 locale 格式特殊,建议换用上述其他适配器,或通过继承DateAdapter类(@angular/material/core)自行实现。
7.3 自定义解析与显示格式:MAT_DATE_FORMATS
Timepicker 使用MAT_DATE_FORMATS对象解析与显示日期对象,其格式最终透传给DateAdapter,因此提供的格式必须与所选适配器兼容。MAT_DATE_FORMATS与mat-datepicker共用同一个令牌——如果应用已在用 datepicker,通常已配置好,但timepicker 额外要求以下三个字段必须存在:
display.timeInputdisplay.timeOptionLabelparse.timeInput
以内置的MAT_NATIVE_DATE_FORMATS为例(native-date-formats.ts):
export const MAT_NATIVE_DATE_FORMATS: MatDateFormats = { parse: { dateInput: null, timeInput: null, // 解析时间输入 }, display: { dateInput: {year: 'numeric', month: 'numeric', day: 'numeric'}, timeInput: {hour: 'numeric', minute: 'numeric'}, // 输入框显示格式 monthYearLabel: {year: 'numeric', month: 'short'}, dateA11yLabel: {year: 'numeric', month: 'long', day: 'numeric'}, monthYearA11yLabel: {year: 'numeric', month: 'long'}, timeOptionLabel: {hour: 'numeric', minute: 'numeric'}, // 下拉选项显示格式 }, };validateAdapter(util.ts)会在组件构造时校验适配器与格式:缺少DateAdapter/MAT_DATE_FORMATS提供者、或上述三个 time 格式字段缺失时,会抛出下文"疑难解答"中对应的错误。
如果想使用官方适配器但换成自己的格式,有两种方式:把格式对象传给 providers 函数,或自行提供MAT_DATE_FORMATS令牌:
bootstrapApplication(MyApp, { providers: [provideNativeDateAdapter(MY_NATIVE_DATE_FORMATS)], });八、无障碍(Accessibility)
Timepicker 实现了 ARIA combobox 交互模式,其 ARIA 角色分配如下:
- 输入框:
role="combobox"、aria-haspopup="listbox",并根据面板状态动态维护aria-expanded、aria-controls、aria-activedescendant(见 timepicker-input.ts 的宿主绑定与_ariaActiveDescendant/_ariaExpanded/_ariaControls三个 computed 信号); - 面板容器:
role="listbox"(见 timepicker.html); - 面板内每个选项:
role="option"(由mat-option提供)。
默认情况下,listbox 通过所在mat-form-field的标签来标注(_getAriaLabelledby()会回退到ariaLabelledby或输入框的getLabelId(),见 timepicker.ts)。如果不使用表单字段或想自定义标签,可通过mat-timepicker的ariaLabel/ariaLabelledby输入设置。
键盘交互方面,面板打开后由ActiveDescendantKeyManager驱动方向键导航(支持 Home/End、PageUp/PageDown、纵向方向键,见 timepicker.ts),Enter选中当前项、Escape关闭、Tab关闭面板,且在关闭时调用scrollOptionIntoView保证活动项可见(_handleKeydown,timepicker.ts)。
九、疑难解答(Troubleshooting)
官方指南总结了 5 类常见错误及解决方案,以下全部可在源码中找到对应的抛错点:
MatTimepicker: No provider found for DateAdapter/MAT_DATE_FORMATS未提供 Timepicker 工作所需的注入项。解决:在应用配置中加入provideNativeDateAdapter()或provideMomentDateAdapter()等适配器(见 util.ts 的missingAdapterError)。MatTimepicker: Incomplete MAT_DATE_FORMATS has been provided提供的MAT_DATE_FORMATS缺少display.timeInput、display.timeOptionLabel或parse.timeInput字段。解决:按上文 7.3 节补齐这三个字段(见 util.ts)。Cannot specify both the options and interval inputs at the same timeoptions与interval互斥,模板中需移除其中一个(见 timepicker.ts)。Value of options input cannot be an empty arrayoptions不能为空数组,否则用户无任何可选项(见 timepicker.ts)。A MatTimepicker can only be associated with a single input同一个<mat-timepicker>只允许一个<input>通过matTimepicker属性关联;当第二个输入框试图注册时抛出(见 timepicker.ts 的registerInput)。
十、深入源码:面板的生命周期与值回写
最后从源码层面对照 API 报告,理解几个关键机制的实现(均为可验证的实现事实):
- 打开流程:
open()先聚焦输入框,随后_generateOptions()按当前interval/options与min/max生成选项,再通过 CDK Overlay 创建浮层(createFlexibleConnectedPositionStrategy,首选"输入框下方对齐"、次选"上方对齐"并附加mat-timepicker-above类),使用TemplatePortal挂载面板模板,并注册detachments、keydownEvents、outsidePointerEvents三个订阅(timepicker.ts)。 - 选中回写:
_selectValue(option)先关闭面板、同步选中态,然后先调用_input().timepickerValueAssigned(option.value)让输入框更新表单控件,再发射selected事件(timepicker.ts);输入框侧timepickerValueAssigned比较sameTime后经_assignUserSelection走_onChange传播给表单并更新valuemodel(timepicker-input.ts)。 - 键盘直达:输入框聚焦时按上/下方向键会直接
open()(_handleKeydown,timepicker-input.ts),Escape可清空当前值。 - locale 联动:
DateAdapter.localeChanges被订阅后,若面板处于打开状态会重新生成选项、输入框在失焦状态下会重新格式化显示值,保证语言切换即时生效(timepicker.ts)。 - 滚动策略:
MAT_TIMEPICKER_SCROLL_STRATEGY默认提供createRepositionScrollStrategy(timepicker.ts),面板打开期间页面滚动时会跟随输入框重定位。
结语
本文以公开 API 报告 goldens/material/timepicker/index.api.md 为主线,完整覆盖了MatTimepicker、MatTimepickerInput、MatTimepickerToggle三个构件及MAT_TIMEPICKER_CONFIG、MAT_TIMEPICKER_SCROLL_STRATEGY两个令牌的用法,并延伸到表单集成、日期选择器组合、校验、选项定制、国际化与无障碍。实战中只需记住三件事:用matTimepicker绑定输入框、按需提供日期适配器(provideNativeDateAdapter起步)、用interval/options控制面板选项,即可在表单中快速交付专业的时间选择体验。
【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/components
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考