Angular 4输入框每两位自动加短横线(生日mm-dd-yyyy格式)
我来帮你解决这个出生日期输入掩码的问题~你之前的指令逻辑是每两位就分割加横线,这才导致了奇怪的格式,我们需要针对mm-dd-yyyy的固定格式来调整掩码规则,同时还要适配ngbDatepicker的对象绑定特性。
解决方案步骤
1. 修正自定义指令的掩码逻辑
我们需要按照mm-dd-yyyy的结构来处理输入,而不是简单的每两位分割。修改后的指令会先过滤非数字字符,再根据输入长度分段添加短横线,同时同步绑定的日期对象:
import { Directive, HostListener, ElementRef, OnInit } from '@angular/core'; import { NgModel } from '@angular/forms'; @Directive({ selector: '[dobMask]' }) export class DobDirective implements OnInit { private el: HTMLInputElement; private lastValue: string = ''; constructor(private elementRef: ElementRef, private ngModel: NgModel) { this.el = this.elementRef.nativeElement; } ngOnInit() { // 初始化时如果有绑定的日期对象,转换成标准显示格式 if (this.ngModel.value) { const { month, day, year } = this.ngModel.value; this.el.value = this.formatDateToDisplay(month, day, year); this.lastValue = this.el.value; } } @HostListener('input', ['$event']) onInput(event: KeyboardEvent) { const input = event.target as HTMLInputElement; // 过滤所有非数字字符,只保留纯数字内容 let rawValue = input.value.replace(/\D/g, ''); // 限制最多8位数字(对应mm-dd-yyyy的8位数字) if (rawValue.length > 8) { rawValue = rawValue.substr(0, 8); } // 根据输入长度构建格式化后的字符串 let formattedValue = ''; switch (rawValue.length) { case 1: case 2: formattedValue = rawValue; break; case 3: case 4: formattedValue = `${rawValue.substr(0, 2)}-${rawValue.substr(2)}`; break; default: formattedValue = `${rawValue.substr(0, 2)}-${rawValue.substr(2, 2)}-${rawValue.substr(4)}`; break; } // 只有当值真正变化时才更新,避免循环触发输入事件 if (formattedValue !== this.lastValue) { this.el.value = formattedValue; this.lastValue = formattedValue; // 将格式化后的字符串转换成NgbDateStruct对象,同步到ngModel this.syncToModel(formattedValue); } } private formatDateToDisplay(month: number, day: number, year: number): string { // 补零处理,确保月份和日期是两位数字 const mm = month.toString().padStart(2, '0'); const dd = day.toString().padStart(2, '0'); const yyyy = year.toString(); return `${mm}-${dd}-${yyyy}`; } private syncToModel(formattedValue: string) { // 拆分格式化字符串,转换成组件绑定的日期对象结构 const parts = formattedValue.split('-'); if (parts.length === 3 && parts[0].length === 2 && parts[1].length === 2 && parts[2].length === 4) { this.ngModel.control.setValue({ month: parseInt(parts[0], 10), day: parseInt(parts[1], 10), year: parseInt(parts[2], 10) }); } else if (parts.length === 2 && parts[0].length === 2) { this.ngModel.control.setValue({ month: parseInt(parts[0], 10), day: parts[1] ? parseInt(parts[1], 10) : null, year: null }); } else if (parts.length === 1) { this.ngModel.control.setValue({ month: parts[0] ? parseInt(parts[0], 10) : null, day: null, year: null }); } else { this.ngModel.control.setValue(null); } } }
2. 调整HTML模板
更新模板中的校验逻辑(因为现在ngModel绑定的始终是对象而非字符串),确保指令和ngbDatepicker正常配合:
<input ngbDatepicker dobMask #d="ngbDatepicker" #dobF="ngModel" class="form-control input-underline input-lg" id="dob" [(ngModel)]="dateOfBirth" placeholder="mm-dd-yyyy" name="dp" [ngClass]="{ invalid: (dobF.value === null || !dobF.value.year || futureDate) && dobF.touched }" required />
关键改进点说明
- 精准分段逻辑:按照
mm-dd-yyyy的结构,在输入到第3位时添加第一个短横线,第5位时添加第二个短横线,完全匹配目标格式 - 纯数字过滤:自动移除输入中的非数字字符,避免无效字符干扰格式
- 双向同步:既保证输入框显示格式化后的字符串,又同步更新组件中的
dateOfBirth对象;初始化时已有日期也会自动格式化显示 - 避免循环触发:通过记录上一次输入值,只有当内容真正变化时才更新,防止输入事件无限循环
额外优化提示
你还可以在指令中添加日期合法性校验,比如限制月份在1-12之间、日期符合对应月份的天数,进一步提升输入体验。
内容的提问来源于stack exchange,提问作者user10796734
相关产品推荐
相关产品推荐

