自定义验证器指令与其他指令结合多次触发

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 中是否被视为无效:

  • 如果小写字母无效,则永远不要为您的 ViewModel 分配小写字母值 iban 属性 - plunker
  • 如果小写有效,则做不区分大小写的iban验证器-plunker