基于 IP 地址正则表达式的反应式表单有效性



更新:我发现使用简单的正则表达式(例如/^[0-9]+$/(可以重现此行为。它也可以使用自定义验证器函数重现,例如

export function textareaPattern(pattern: RegExp): ValidatorFn {
return (control: AbstractControl): {[key: string]: any} | null => {
const values = control.value.split('n');
let mismatch = false;
values.forEach(val => {
mismatch = !pattern.test(val);
if(mismatch) {
return {textareaPattern: {value: control.value}}
}
});
return null;
}
}

原帖:我有一个正则表达式,旨在验证用户是否在文本区域中输入了多个有效的 IP 地址(每行一个(。

/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(/([0-9]|[1-2][0-9]|3[0-2]((?$?/GIM

正则表达式在一条快乐的道路上工作,但是一旦同时存在有效条目和无效条目,就会变得奇怪。

如果我输入所有有效值,则窗体控件按预期有效。例:

10.1.121.2
10.2.121.3
10.3.121.4

如果我输入所有无效值,则表单控件无效,正如预期的那样。例:

butter
potato
123

如果我输入有效值和无效值的组合,则表单控件将在有效和无效之间交替,具体取决于条目的长度。如果条目的长度为偶数,则无效,奇数有效。真奇怪。

长度: 12, 无效

10.1.121.1
f

长度: 13, 有效

10.1.121.1
fo

我认为问题出在正则表达式上,但我承认我远不能破译正则表达式的庞然大物。

正则表达式应匹配:

192.168.1.1
255.203.232.1
1.0.12.1

正则表达式不应匹配:

256.1.12.1
1934.1.22.3
201.foobar.201.1

最小、可重现的示例(使用解决方案更新(:https://stackblitz.com/edit/angular-v4f35a?file=src%2Fapp%2Fapp.component.ts

所以这归结为两个问题。

第一个问题是 RegExes 的global标志导致 JS 引擎跟踪 lastIndex 属性,当再次调用myRegex.test()时,下一次搜索将从该属性开始。由于正则表达式对象在整个验证过程中是相同的,因此在您键入时会更新和引用 lastIndex,并且表单会重新验证。

第二个问题是,如果没有global,只要一行是正确的,表单就会被认为是有效的。所以我的输入可能是

192.168.foobar
192.168.1.1
hello world!

并且由于第 2 行,表单有效。

我的解决方案

我已经为文本区域实现了一个自定义验证器。它接受我的正则表达式(没有g标志(并在n上拆分文本区域的内容。它立即假设没有不匹配,然后针对每一行检查该行是否与正则表达式匹配。如果不是,它将不匹配并返回一个包含文本区域内容的对象:

import {AbstractControl, ValidatorFn} from '@angular/forms';
export function multilinePattern(pattern: RegExp): ValidatorFn {
return (control: AbstractControl): {[key: string]: any} | null => {
const values = control.value.split('n');
if (values.length === 1 && values[0] === '') {
return null;
}
let mismatch = false;
values.forEach((val: string) => {
if (!pattern.test(val)) {
mismatch = true;
}
});
return mismatch ? {multilinePattern: {value: control.value}} : null;
};
}

最新更新