ngModel - 如何在不使其变脏的情况下操纵初始模型值?

时间:2015-05-29 20:42:37

标签: angularjs angular-ngmodel

让我们说我需要一个指令来获取用户在输入中键入的内容并将其转换为小写。

所以我想出了一个像这样的指令(在这个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并导致$解析器管道,我也会这样做。希望它意识到用户实际上并没有与它进行交互(从而使它保持原始状态)。

这可能吗?如果是这样,有什么相关的最佳实践(比如可以从$ format调用$ setViewValue - 直接覆盖ngModel?等等......

3 个答案:

答案 0 :(得分:3)

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

在更新viewvalue之前,只需保存对$ setDirty函数的引用,并在ngModelCtrl中用空函数替换它。设置viewvalue后,我恢复原来的功能。

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

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

注意:我没有使用自定义ng-model-options。我没有尝试过,但我确定如果你使用自定义去抖超时,这个解决方案对你不起作用,但我认为可以设法在去抖时间之后调用ngModelController.$setDirty = tmp;行已过期。

答案 1 :(得分:0)

查看this plunk

app.directive('lowerCaseInput', function() {
  return {
    restrict: 'A', // as attribute to 'decorate' normal input
    require: 'ngModel',
    link: function(scope, el, attrs, ngModel) {
      // view-> model
      ngModel.$parsers.push(toLowerCase);
      // model->view
      ngModel.$formatters.push(toLowerCase);

      function toLowerCase(value) {
        return value && value.toLowerCase();
      }

      // if input value should reflect modelValue
      el.on('blur', function () {
        if (ngModel.$viewValue !== ngModel.$modelValue) {
          ngModel.$setViewValue(ngModel.$modelValue);
          ngModel.$render();
        }
      });
    }
  }
})

首先:您的解决方案过于复杂,因为您创建了两个ngModel实例,而您可以轻松地装饰&#39;普通文字输入。在您的实现中,您将所有逻辑分配给指令元素上的ngModel,而不是模板中输入的ngModel。

我的解决方案不需要在链接阶段操作ngModel,因为这个作业留给了$ formatters。但是如果您真的需要在不创建ngModel $dirty的情况下操纵模型值,您可以执行以下操作:

var isPristine = ngModel.$pristine;
ngModel.$setViewValue(something);
if (isPristine) { ngModel.$setPristine(); }

编辑:

我的解决方案正确设置了$viewValue,但$modelValue仍然保持不正确的值。 更正确的解决方案is here但它远离&#34; ok&#34; IMO。

答案 2 :(得分:0)

这是指令所需要的。

app.directive('lowerCaseInput', function() {
  function toLowerCase(value) {
    return value && value.toLowerCase();
  }

  return {
    restrict: 'E',
    template: '<input ng-model="getterSetter" ng-model-options="{getterSetter : true}">',
    scope: {
      ngModel: '='
    },
    controller: function($scope) {
      $scope.getterSetter = function(value) {
        return $scope.ngModel = toLowerCase(value || $scope.ngModel);;
      }
    }
  }
})

检查Plunker here和 文档here