复杂模型的角度验证问题

时间:2016-07-14 12:24:07

标签: javascript angularjs forms validation

我的设置: Angular 1.4.4

我有一个简单的表单,如下所示:

<div ng-controller="MyCtrl3">
  <datepick ng-model="model.carA" foo3="model.carB"></datepick>

  <datepick ng-model="model.carB" foo3="model.carA"></datepick>
  <pre> {{ model | json }} </pre>
</div>

datepick指令如下所示:

myApp.directive('datepick', function() {
  return {
    restrict: 'E',
    scope: {
      ngModel: '='
    },
    template: '<div ng-if="true"><input type="text" ng-model="ngModel.bar"/></div>'
  };
});

所以我使用bar属性将属性ng-model传递给它。直到现在一切都按预期工作。模型与渲染形式的变化同步。

下一步我想引入验证来验证当前指令的基础模型上的属性bar以及MyCtrl3中的一些其他对象 - model。我为此创建了属性指令foo3

myApp.directive('foo3', function() {
  return {
    restrict: 'A',
    require: "ngModel",
    link: function(scope, element, attrs, controller) {

      scope.$watch(attrs.foo3, function(newValue, oldValue) {
        // New Value of comparison field
        console.log("New valued for comparison model: " + JSON.stringify(newValue));
        // Current value of undelying movelValue
        console.log("Current value for undelying model" + JSON.stringify(controller.$modelValue));

        controller.$validate();
      }, true);

      var validateMoreThanDate = function(modelValue, viewValue) {
        let viewValueObject = modelValue;
        var comparisonModel = scope.$eval(attr.moreThanDateObject);

        if ((!viewValueObject && !viewValueObject.bar) ||
          (!comparisonModel && !comparisonModel.bar)) {
          // It's valid because we have nothing to compare against
          return true;
        }
        // It's valid if model is lower than the model we're comparing against
        return viewValueObject.bar > comparisonModel.bar;
      };

      controller.$validators['moreThanDateObject'] = validateMoreThanDate;
    }
  };
});

正如您所看到的,我将模型作为参数传递给指令foo3。监视器跟踪每个更改,但模型更改时不会触发验证器。

问题:如何在我的案例中验证这些字段? (这里有jsFiddle来检查实时代码:https://jsfiddle.net/ichyr/b1jqfkj1/

N.B。我知道当对象中的模型和此对象的属性发生更改时,不会触发$ parsers和$ formatters管道:

  

如果新值是一个对象(而不是字符串或数字),我们   应该在将对象传递给$ setViewValue之前复制该对象。   这是因为ngModel不会对对象进行深度监视   只寻找身份的变化。如果您只更改属性   对象然后ngModel将不会意识到该对象具有   已更改,不会调用$ parsers和$ validators管道。

所以也许$ validators也没有触发,但controller.$validate()的添加对$ watch表达没有帮助。

2 个答案:

答案 0 :(得分:0)

我使用 $ watch ers成功完成此任务,而无需执行任何$ parsers,$ formatters或$ validators。如果属性发生变化,前两个肯定不能使用复杂模型。最后一个似乎也不起作用。

我已经通过datepickng-model + foo3指令在foo3指令中引用了两种型号的两款手表,并创建了小型验证功能。所以myApp.directive('foo3', function() { return { restrict: 'A', require: "ngModel", link: function(scope, element, attrs, controller) { scope.$watch(attrs.foo3, function(newValue, oldValue) { var isValid = validateMoreThanDate(controller.$modelValue, newValue); controller.$setValidity('moreThanDateObject', isValid); }, true); scope.$watch(attrs.ngModel, function(newValue, oldValue) { var isValid = validateMoreThanDate(newValue, scope.$eval(attrs.foo3)); controller.$setValidity('moreThanDateObject', isValid); }, true); var validateMoreThanDate = function(underlyingModel, comparisonModel) { if ((!underlyingModel && !underlyingModel.bar) || (!comparisonModel && !comparisonModel.bar)) { // It's valid because we have nothing to compare against return true; } // It's valid if model is lower than the model we're comparing against return underlyingModel.bar > comparisonModel.bar; }; } }; }); 验证属性指令现在看起来像这样:

// Directive to format the data in the text inputs
myApp.directive('numberInputParser', function() {
  return {
    restrict: 'A',
    require: "ngModel",
    link: function(scope, element, attrs, controller) {
      debugger;
      controller.$parsers.unshift(function(data) {
        return parseInt(data);
      });

      controller.$formatters.unshift(function(data) {
        debugger;
        return data;
      });
    }
  };
});

此外,我还创建了一个小的装饰指令,用于格式化和解析输入inself中的值(测试类型导致字符串值):

datepick

并且number-input-parser指令的代码被尊重地更改了(添加了myApp.directive('datepick', function() { return { restrict: 'E', scope: { ngModel: '=' }, template: '<div ng-if="true"><input type="text" ng-model="ngModel.bar" number-input-parser/></div>' }; }); 指令):

{{1}}

可以在此处看到生成的代码:https://jsfiddle.net/ichyr/f1qeey37/

答案 1 :(得分:0)

N.B。我的同事提出了更好的方法。

可以改进此解决方案。我们可以在datepick指令中使用自定义getter / setter函数的人工对象来省略$watchers的使用,如下所示:

myApp.directive('datepick', function() {
  return {
    restrict: 'E',
    scope: {
      ngModel: '='
    },
    template: '<div ng-if="model.open"><input type="text" ng-model="model.value"/></div>',
    link: function(scope) {
      scope.model = {
        get value() {
          return scope.ngModel;
        },
        set value(newValue) {
          scope.ngModel = parseInt(newValue);
        },
        open: true
      };
    }
  };
});

当我们在那里传递对象时,这克服了ng-if范围中变量的阴影,而不是原始的。

这种方式带来了以下好处:

  1. 无需将对象传递给datepick指令
  2. 删除了几个$watch表达式,因为我们正在破坏本机JS功能(从而提高性能)
  3. 允许使用原语作为datepicker的参数(模型),以便能够利用angular.js $ parsers $ formatters $ validators (如果模型是对象,它们不起作用/检测对象属性的变化)
  4. getter和setter不能代替angular $ parsers $ formatters
  5. 您可以在此处找到最终的小提琴:https://jsfiddle.net/ichyr/suncp2gf/