假设我想要一个指令,它接受用户输入的内容并将其变为小写。

所以我想出了一个像这样的指令(在这个Plunker http://plnkr.co/edit/jnE3s8MRr1tFCX0WVYel?p=preview中):

app.directive('lowerCaseInput', function() {
  return {
    restrict: 'E',
    template:'<input ng-model="vm.input" />',
    scope: {},
    require: ['ngModel', 'lowerCaseInput'],
    controller:function() {},
    link: function(scope, el, attrs, ctrls) {
      var ngModel = ctrls[0], vm = ctrls[1];
      ngModel.$render = render;

        // view-> model
      ngModel.$parsers.push(function(value) {
        return toLowerCase(value);
      });

        // model->view
      ngModel.$formatters.push(function(value) {
        return value;
      });

      function render() {
        vm.input = ngModel.$viewValue;
      }

      // view -> model && model-> view
      function toLowerCase(value) {
        return value && value.toLowerCase();
      }

      scope.$watch('vm.input', function(newVal, oldVal) {
        if(newVal !== oldVal)
          ngModel.$setViewValue(newVal);
        });
    },
    controllerAs: 'vm'
  }
})


但是,如您在演示中所看到的,因为从未触发setViewValue,所以模型值不会在初始遍历中得到反映(变成小写)。在这种情况下,我确实希望指令根据某些规则更改模型,是否应该遵循一般方法?

此外,我想避免在用户与之实际交互之前将控件变为脏的,因此即使我确实调用了$ setViewValue并导致了$ parsers管道,我也希望它能够意识到用户实际上并没有与之交互(因此将其保留为$ pristine)。

这可能吗?如果是这样,有什么最佳实践(例如可以从$ format调用$ setViewValue吗–直接覆盖ngModel吗?

最佳答案

以下解决方案适用于我(使用Angular 1.4)

在更新viewvalue y之前,只需保存对$ setDirty函数的引用,然后在ngModelCtrl中将其替换为空函数。设置viewvalue之后,我恢复了原始功能。

var tmp = ngModelController.$setDirty;
ngModelController.$setDirty = angular.noop;
ngModelController.$setViewValue(value);
ngModelController.$setDirty = tmp;


这对我有用,因为我能够在自定义指令中设置默认值,并且输入字段和表单控制器都保持$ pristine。

当心:我没有使用自定义的ng-model-options。我没有尝试过,但是我确定如果您使用自定义防抖动超时,则该解决方案将无法为您服务,但是我认为可以在防抖动时间到期后调用ngModelController.$setDirty = tmp;行。

10-05 23:22