是否可以在没有深度$ watch的指令中检测ng-model的变化

时间:2016-09-06 10:11:41

标签: javascript angularjs

我正在编写一个带有自定义验证逻辑的指令来验证对象。

HTML:

<input type="hidden" name="obj" ng-model="vm.obj" validate-object />

JS:

angular
  .module('myApp')
  .directive('validateObject', function () {
    return {
      restrict: 'A',
      require: 'ngModel',
      link: function (scope, element, attrs, ngModelCtrl) {
        ngModelCtrl.$validators.validateObject = myValidator;

        function myValidator (modelValue, viewValue) {
          return validateObject(modelValue);
        }

        function validateObject (obj) {
          // Look inside the object
        }
      }
    }
  });

问题是当对象内的属性发生更改时,验证程序不会运行。

我可以使用$watch添加objectEquality === true,然后使用我的验证逻辑手动$setCustomValidity。像这样:

link: function (scope, element, attrs, ngModelCtrl) {
  scope.$watch(attrs.ngModel, onModelChange, true);

  function onModelChange (newValue) {
    ngModelCtrl.$setCustomValidity('validateObject', validateObject(newValue))
  }

  function validateObject (obj) {
    // Look inside the object
  }
}

但我不喜欢使用旧学校的方式手动使用$setValidity,另外添加手册$watch,而NgModelController已经有了在更新过程中注册的方法(如{ {1}}),此外$formatters是一个可能存在性能问题的深层问题。

我错了吗?还有更好的方法吗?

1 个答案:

答案 0 :(得分:2)

来自https://github.com/angular/angular.js/blob/master/src/ng/directive/ngModel.js#L699

if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
  return;
}

ngModel对旧版本的模型执行平面相等检查,因此对象内的任何更改都不会反映在ngModel或ngChange上。

优化方法是使用不可变数据,这意味着每次更改模型(对象)时,都要创建一个新副本:

function changeModel(){
  this.vm.name = "roy";
  // Create a new object for ngModel;
  this.vm = angular.copy(this.vm);
}

修改

我记得我以前解决了以前的问题。您希望将一组ng-model绑定到对象的属性,并为整个对象设置1个更改侦听器。

这是我的解决方案:http://plnkr.co/edit/6tPMrB8n1agINMo252F2?p=preview

我所做的是创建一个必须放在表单元素上的新指令“formModel”。 Angular有一个带有控制器的form指令。 NgModelController需要一个父表单控制器,然后它将自己添加到表单中(这就是你在整个表单上获得有效性的方式)。 所以在我的指令中,我修饰了表单的$ addControl方法,并为每个通过$ viewChangeListeners添加自己的ngModelController添加了一个监听器,现在在表单内的每次更改ngModel时,formModel指令将复制整个对象并触发$ setViewValue