自定义验证器指令与其他指令结合多次触发
Custom validator directive combined with other directive fires multiple times
我有一个自定义验证指令 iban
,它检查值是否有效(荷兰语)IBAN。该验证器需要将值大写。我还有一个 uppercase
指令,它将值更改为大写。我想将两者结合在一个输入元素上:
<input type="text" ng-model="iban" name="iban" capitalize="" iban="" />
我创建了一个 jsfiddle 演示情况。
我正在努力寻找正确的执行顺序。如果用户输入一个值,我希望大写先触发,因此 iban 验证器会收到一个大写的值。如果模型值是代码设置的,我也想先大写。
当用户输入小写字符时,大写指令会调用 ctrl.$setViewValue 来设置视图值。这会通过解析器触发另一个 运行。所以 uppercase 指令和 iban 指令都被执行了两次。控制台日志显示:
parsers.capitalize: nL12HHBA0429672071
uppercasing: nL12HHBA0429672071 => NL12HHBA0429672071, setting view value
parsers.capitalize: NL12HHBA0429672071
uppercasing: NL12HHBA0429672071 already uppercased
parsers.iban: NL12HHBA0429672071
setting validity to: true
returning NL12HHBA0429672071
parsers.iban: NL12HHBA0429672071
setting validity to: true
returning NL12HHBA0429672071
我认为多次循环遍历您的解析器并不是本意。
另一个问题是当我将代码中的值设置为已经大写的无效 IBAN(我的 fiddle 中的最后一个 link)。在那种情况下,大写指令将不必做任何事情。 iban 指令格式化程序会将有效性设置为 false,并且只是 return 值。如果它是无效的小写 IBAN,则大写指令将调用 setViewValue,导致执行 IBAN 指令解析器代码,这将 return undefined
。所以这种情况会将模型的值更改为未定义。
我是不是把事情搞得太复杂了?我是否应该尝试只创建一个 iban 指令,以确保在用户输入有效的小写 iban 值时将大写值存储在模型中?如果它是从代码设置的,我应该只保留模型中的小写值吗?也许只是在元素上使用 style="text-transform: uppercase"
来始终将值显示为大写?缺点是如果模型设置为有效但小写的值,表单将显示大写的值,这将是有效的,而模型值实际上无效。
这里肯定有些复杂。在玩弄时,也有一些 Angular 怪异(至少在我看来 - 我会讲到的)。
这里介绍的一个复杂性是您的 capitalize
$formatter
实际上更改了模型值。我认为这违背了格式化程序函数的意图(在 model -> view
方向上转换值)。 View(以及通过其指令存在于 View 中的格式化程序)应该仅在更改源自 View 时更改模型。这使模型成为真实的来源,如果它被设置为无效值,那么就这样吧 - 有效性应该反映在视图中,但它不应该尝试 "fix" 模型。
考虑到这一点,让我们也使用 $validators
进行验证(而不是 $parsers/$formatters 管道):
.directive("iban", function(){
return {
require: "?ngModel",
link: function(scope, element, attrs, ngModel){
if (!ngModel) return;
ngModel.$validators.iban = function(modelValue, viewValue){
// after parser ran, validate the resulting modelValue
return validate(modelValue);
};
function validate(val){
return modelValue === "VALID IBAN"; // for the sake of example
}
}
};
});
$parsers
(更改模型值)和 $formatters
(更改视图值)在 $validators
运行.
之前调用
另一个复杂性(看起来像 Angular 的怪异之处)是您的 capitalize
格式化程序可以使 $viewValue
对无效的 $modelValue
有效].这本身行为正确 - 它格式化 $viewValue
并将有效性保持为假(因为模型是假的)。但是,如果您现在将模型更改为当前设置的(有效的)$viewValue
,那么 Angular 决定跳过 (src) 验证器(因为它发现新的和old $viewValues
),因此尽管模型和视图值都有效,但有效性永远不会生效。同样,对于有效到无效的情况(无效的小写值永远不会使模型无效)。
但是,这种情况很少见,如果编码得当,应该完全避免这种情况。为什么?因为模型应该很少(如果有的话)假定无效值并且应该在有效范围内运行。
ng-model
通过为无效值将模型设置为 undefined
来确保这一点(默认情况下,除非您 allowInvalid
)。
因此,对于您的问题,请确定小写 IBAN 在您定义的 ViewModel 中是否被视为无效:
我有一个自定义验证指令 iban
,它检查值是否有效(荷兰语)IBAN。该验证器需要将值大写。我还有一个 uppercase
指令,它将值更改为大写。我想将两者结合在一个输入元素上:
<input type="text" ng-model="iban" name="iban" capitalize="" iban="" />
我创建了一个 jsfiddle 演示情况。
我正在努力寻找正确的执行顺序。如果用户输入一个值,我希望大写先触发,因此 iban 验证器会收到一个大写的值。如果模型值是代码设置的,我也想先大写。
当用户输入小写字符时,大写指令会调用 ctrl.$setViewValue 来设置视图值。这会通过解析器触发另一个 运行。所以 uppercase 指令和 iban 指令都被执行了两次。控制台日志显示:
parsers.capitalize: nL12HHBA0429672071
uppercasing: nL12HHBA0429672071 => NL12HHBA0429672071, setting view value
parsers.capitalize: NL12HHBA0429672071
uppercasing: NL12HHBA0429672071 already uppercased
parsers.iban: NL12HHBA0429672071
setting validity to: true
returning NL12HHBA0429672071
parsers.iban: NL12HHBA0429672071
setting validity to: true
returning NL12HHBA0429672071
我认为多次循环遍历您的解析器并不是本意。
另一个问题是当我将代码中的值设置为已经大写的无效 IBAN(我的 fiddle 中的最后一个 link)。在那种情况下,大写指令将不必做任何事情。 iban 指令格式化程序会将有效性设置为 false,并且只是 return 值。如果它是无效的小写 IBAN,则大写指令将调用 setViewValue,导致执行 IBAN 指令解析器代码,这将 return undefined
。所以这种情况会将模型的值更改为未定义。
我是不是把事情搞得太复杂了?我是否应该尝试只创建一个 iban 指令,以确保在用户输入有效的小写 iban 值时将大写值存储在模型中?如果它是从代码设置的,我应该只保留模型中的小写值吗?也许只是在元素上使用 style="text-transform: uppercase"
来始终将值显示为大写?缺点是如果模型设置为有效但小写的值,表单将显示大写的值,这将是有效的,而模型值实际上无效。
这里肯定有些复杂。在玩弄时,也有一些 Angular 怪异(至少在我看来 - 我会讲到的)。
这里介绍的一个复杂性是您的 capitalize
$formatter
实际上更改了模型值。我认为这违背了格式化程序函数的意图(在 model -> view
方向上转换值)。 View(以及通过其指令存在于 View 中的格式化程序)应该仅在更改源自 View 时更改模型。这使模型成为真实的来源,如果它被设置为无效值,那么就这样吧 - 有效性应该反映在视图中,但它不应该尝试 "fix" 模型。
考虑到这一点,让我们也使用 $validators
进行验证(而不是 $parsers/$formatters 管道):
.directive("iban", function(){
return {
require: "?ngModel",
link: function(scope, element, attrs, ngModel){
if (!ngModel) return;
ngModel.$validators.iban = function(modelValue, viewValue){
// after parser ran, validate the resulting modelValue
return validate(modelValue);
};
function validate(val){
return modelValue === "VALID IBAN"; // for the sake of example
}
}
};
});
$parsers
(更改模型值)和 $formatters
(更改视图值)在 $validators
运行.
另一个复杂性(看起来像 Angular 的怪异之处)是您的 capitalize
格式化程序可以使 $viewValue
对无效的 $modelValue
有效].这本身行为正确 - 它格式化 $viewValue
并将有效性保持为假(因为模型是假的)。但是,如果您现在将模型更改为当前设置的(有效的)$viewValue
,那么 Angular 决定跳过 (src) 验证器(因为它发现新的和old $viewValues
),因此尽管模型和视图值都有效,但有效性永远不会生效。同样,对于有效到无效的情况(无效的小写值永远不会使模型无效)。
但是,这种情况很少见,如果编码得当,应该完全避免这种情况。为什么?因为模型应该很少(如果有的话)假定无效值并且应该在有效范围内运行。
ng-model
通过为无效值将模型设置为 undefined
来确保这一点(默认情况下,除非您 allowInvalid
)。
因此,对于您的问题,请确定小写 IBAN 在您定义的 ViewModel 中是否被视为无效: