在一个指令中格式化和验证
Format and Validate within one directive
我正在尝试构建一个指令来处理处理持续时间的表单。我希望表单显示一个文本框,该文本框的格式类似于 1:24,但总分钟数的值 - 在本例中为 144(这将有助于以后添加很多时间!)。
这是我当前的代码:
@Directive({
...
)
export class DurationFormatDirective implements ControlValueAccessor {
@HostBinding('value') inputValue;
mins: number;
onChange;
onTouched;
constructor() {}
@HostListener('blur', ['$event.target.value'])
onBlur(value: string) {
let hrs: number, mins: number;
if (value === '' || value === '0') {
hrs = 0;
mins = 0;
} else {
if (value.indexOf(':') === -1) { // there is no colon
if (value.length <= 2) {
hrs = 0;
mins = +value;
} else {
hrs = +value.substring(0, value.length - 2);
mins = +value.substring(value.length - 2);
}
} else { // There is a colon
const arr = value.split(':');
hrs = +arr[0];
mins = +arr[1];
}
}
this.mins = hrs * 60 + mins;
this.inputValue = `${hrs}:${mins < 10 ? '0' : ''}${mins}`;
this.onChange(this.mins);
this.onTouched();
}
writeValue(val) {
this.mins = val;
const hrs = Math.floor(val / 60);
const mins = val % 60;
this.inputValue = `${hrs}:${mins < 10 ? '0' : ''}${mins}`;
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
}
通常这很好用,但是如果用户输入 2:zw 例如 - 它会中断,因为 zw 不是数字。如果输入了无效的(IE Not 0-9 或 :),它应该声明该字段无效并且不尝试对其进行格式化或更新值。我还可以使该指令同时更改有效的 属性 吗?如果它有所作为,我正在使用反应形式。
谢谢
所以我实际上用不同的方式解决了这个问题。
我添加了一个 RegExp 测试来查找字母和一个 if 语句来查找大于 60 的分钟。在任何一种情况下,值都设置为 NaN。
然后我创建了一个非常简单的验证器来检查父表单上的 NaN。
这也让我可以稍后进行另一次验证,检查输入的持续时间是否不大于另一个字段的总持续时间。
指令的完整代码:
@Directive({
selector: '[appTimeFormat]',
})
export class TimeFormatDirective implements ControlValueAccessor {
@HostBinding('value') inputValue;
onChange;
onTouched;
private regex = /^\d{1,2}:?\d{0,2}$/;
constructor() {}
@HostListener('blur', ['$event.target.value'])
onBlur(value: string) {
this.onTouched();
if (!this.regex.test(value)) {
this.onChange(NaN);
return;
}
let hrs: number, mins: number;
if (value === '' || value === '0') {
hrs = 0;
mins = 0;
} else {
if (value.indexOf(':') === -1) {
// There is no colon
if (value.length <= 2) {
hrs = 0;
mins = +value;
} else {
hrs = +value.substring(0, value.length - 2);
mins = +value.substring(value.length - 2);
}
} else {
// There is a colon
const arr = value.split(':');
hrs = +arr[0];
mins = +arr[1];
}
}
this.inputValue = `${hrs}:${mins < 10 ? '0' : ''}${mins}`;
if (mins > 60) {
this.onChange(NaN);
return;
}
this.onChange(hrs * 60 + mins);
}
writeValue(val) {
if (val) {
const hrs = Math.floor(val / 60);
const mins = val % 60;
this.inputValue = `${hrs}:${mins < 10 ? '0' : ''}${mins}`;
} else {
this.inputValue = '';
}
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
}
验证者:
nanValidator(control: FormControl) {
if (isNaN(control.value)) {
return { invalidNumber: true };
}
return null;
}
我正在尝试构建一个指令来处理处理持续时间的表单。我希望表单显示一个文本框,该文本框的格式类似于 1:24,但总分钟数的值 - 在本例中为 144(这将有助于以后添加很多时间!)。
这是我当前的代码:
@Directive({
...
)
export class DurationFormatDirective implements ControlValueAccessor {
@HostBinding('value') inputValue;
mins: number;
onChange;
onTouched;
constructor() {}
@HostListener('blur', ['$event.target.value'])
onBlur(value: string) {
let hrs: number, mins: number;
if (value === '' || value === '0') {
hrs = 0;
mins = 0;
} else {
if (value.indexOf(':') === -1) { // there is no colon
if (value.length <= 2) {
hrs = 0;
mins = +value;
} else {
hrs = +value.substring(0, value.length - 2);
mins = +value.substring(value.length - 2);
}
} else { // There is a colon
const arr = value.split(':');
hrs = +arr[0];
mins = +arr[1];
}
}
this.mins = hrs * 60 + mins;
this.inputValue = `${hrs}:${mins < 10 ? '0' : ''}${mins}`;
this.onChange(this.mins);
this.onTouched();
}
writeValue(val) {
this.mins = val;
const hrs = Math.floor(val / 60);
const mins = val % 60;
this.inputValue = `${hrs}:${mins < 10 ? '0' : ''}${mins}`;
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
}
通常这很好用,但是如果用户输入 2:zw 例如 - 它会中断,因为 zw 不是数字。如果输入了无效的(IE Not 0-9 或 :),它应该声明该字段无效并且不尝试对其进行格式化或更新值。我还可以使该指令同时更改有效的 属性 吗?如果它有所作为,我正在使用反应形式。
谢谢
所以我实际上用不同的方式解决了这个问题。
我添加了一个 RegExp 测试来查找字母和一个 if 语句来查找大于 60 的分钟。在任何一种情况下,值都设置为 NaN。
然后我创建了一个非常简单的验证器来检查父表单上的 NaN。
这也让我可以稍后进行另一次验证,检查输入的持续时间是否不大于另一个字段的总持续时间。
指令的完整代码:
@Directive({
selector: '[appTimeFormat]',
})
export class TimeFormatDirective implements ControlValueAccessor {
@HostBinding('value') inputValue;
onChange;
onTouched;
private regex = /^\d{1,2}:?\d{0,2}$/;
constructor() {}
@HostListener('blur', ['$event.target.value'])
onBlur(value: string) {
this.onTouched();
if (!this.regex.test(value)) {
this.onChange(NaN);
return;
}
let hrs: number, mins: number;
if (value === '' || value === '0') {
hrs = 0;
mins = 0;
} else {
if (value.indexOf(':') === -1) {
// There is no colon
if (value.length <= 2) {
hrs = 0;
mins = +value;
} else {
hrs = +value.substring(0, value.length - 2);
mins = +value.substring(value.length - 2);
}
} else {
// There is a colon
const arr = value.split(':');
hrs = +arr[0];
mins = +arr[1];
}
}
this.inputValue = `${hrs}:${mins < 10 ? '0' : ''}${mins}`;
if (mins > 60) {
this.onChange(NaN);
return;
}
this.onChange(hrs * 60 + mins);
}
writeValue(val) {
if (val) {
const hrs = Math.floor(val / 60);
const mins = val % 60;
this.inputValue = `${hrs}:${mins < 10 ? '0' : ''}${mins}`;
} else {
this.inputValue = '';
}
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
}
验证者:
nanValidator(control: FormControl) {
if (isNaN(control.value)) {
return { invalidNumber: true };
}
return null;
}