【问题标题】:Proper way to $watch external changes to a ngModel from inside a directive从指令内部 $watch 对 ngModel 的外部更改的正确方法
【发布时间】:2015-10-18 12:46:31
【问题描述】:

我正在尝试创建一个从两个输入源读取的指令,并做一些事情将其变成一个。所以要做到这一点,我正在听我的两个输入的变化并将新的组合值分配给我的指令的 ngModel。

问题是我还需要知道何时在指令之外修改 ngModel 以执行相反的过程并正确设置我的两个指令输入源的值。有合适的方法吗?

我做了一个 sn-p 来更好地说明问题,这不是我的实际指令。

angular.module('myapp', [])

.controller('AppController', function($scope, $timeout) {
  $scope.data = {
    value: ''
  };
  
  $timeout(function() {
    $scope.data.value = 'hellooooo';
  }, 5000);
})

.directive('sampleDirective', function() {
  return {
    require: 'ngModel',
    restrict: 'E',
    template: '<input ng-model="data.input1" ng-change="changed()"><input ng-model="data.input2" ng-change="changed()">',
    scope: {
      ngModel: '='
    },
    controller: function($scope) {
      $scope.data = {};
      $scope.changed = function() {
        $scope.ngModel = $scope.data.input1 + ' + ' + $scope.data.input2;  
      }
      
      // The watch is running even when the ngModel is modified inside the changed function above 
      // I want it to only run when the model is changed from outside the directive, like
      // I'm doing in the AppController.
      $scope.$watch('ngModel', function(value) {
        if (value) {
          // Just simulating some processing
          var length = value.length;
          
          $scope.data.input1 = value.slice(0, length/2);
          $scope.data.input2 = value.slice(length/2, length-1);
        }
      });
    }
  };
});
<div ng-app="myapp">
  
  <div ng-controller="AppController">
    <sample-directive ng-model="data.value"></sample-directive>

    <br>
    <br>
    <div>{{ data.value }}</div>
  </div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.3/angular.js"></script>

【问题讨论】:

标签: javascript angularjs angularjs-directive angularjs-scope isolate-scope


【解决方案1】:

你有一个自定义输入控件。它是一个有 2 个文本框的输入控件,但“模型”实际上是两者的结合。

要实现“支持”ngModel 的自定义输入控件,即与 require: ngModel 的其他指令集成,您的指令还需要 require: ngModel 并使用 ngModelController 挂钩来集成。

所以,不要只做scope: { ngModel: "=" }。这样,您就可以双向绑定到附加到 ng-model 属性的模型,但是当您使用 scope.ngModel = "something" 设置此值时,事情不会像您期望的那样工作。

Angular 在其ngModelController 的文档中提供了一个example of a custom input control。简而言之,您需要协调设置$viewValue - 当底层视图更改时,并设置视图 - 当模型更改时。

.directive("sampleDirective", function(){
  return {
    require: "ngModel",
    scope: true,
    template: '<input ng-model="d.input1" ng-change="viewChanged()">\
               <input ng-model="d.input2" ng-change="viewChanged()">',

    link: function(scope, element, attrs, ngModel){
      var d = scope.d = {};      

      ngModel.$render = render;

      scope.viewChanged = read;


      // defines how to render based on model changes
      function render(){
        var modelValue = ngModel.$modelValue || "";
        var length = modelValue.length;

        d.input1 = modelValue.slice(0, length/2);
        d.input2 = length > 1 ? modelValue.slice(length/2, length) : "";
      };


      // defines how to set the model based on DOM changes
      function read(){
        var newViewValue = d.input1 + d.input2;
        ngModel.$setViewValue(newViewValue);
      }
    }
  };
});

然后,您的控件将与支持ngModel 的任何其他指令一起使用,例如requiredng-patternng-change,并且可以参与表单验证:

<div ng-form="form1">
  <sample-directive ng-model="foo" maxlength="8" ng-change="doSomething()">
  </sample-directive>
</div>

Demo

【讨论】:

    【解决方案2】:

    相当奇怪的要求,但您可以更改指令以改用ngModelController

    .directive('sampleDirective', function() {
      return {
        require: 'ngModel',
        restrict: 'E',
        template: '<input ng-model="data.input1" ng-change="changed()"><input ng-model="data.input2" ng-change="changed()">',
        scope: {
          ngModel: '='
        },
        link: function(scope, element, attributes, ngModelCtrl) {
          // This will be called when it is changed from outside the directive
          ngModelCtrl.$formatters.push(function(value) {
            if (value) {
              // Just simulating some processing
              var length = value.length;
    
              scope.data.input1 = value.slice(0, length/2);
              scope.data.input2 = value.slice(length/2, length-1);
            }
            return value;
          });
    
          scope.changed = function() {
            // Set the model value from inside the directive
            ngModelCtrl.$setViewValue(scope.data.input1 + ' + ' + scope.data.input2);
          }
        },
        controller: function($scope) {
          $scope.data = {};
        }
      };
    })
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-23
      • 1970-01-01
      相关资源
      最近更新 更多