【问题标题】:Detect who change the model (user input vs controller) angularjs检测谁更改了模型(用户输入与控制器)
【发布时间】:2016-03-02 14:10:30
【问题描述】:

我有一个连接到模型的输入。此外,输入具有$watch 模型的指令。

模型有两种变化方式。

  1. 用户将在文本框中输入内容。
  2. 代码会改变它(不管是什么原因)

我的问题是

有没有办法在指令中找出谁更改了模型、用户交互或代码?

示例:

angular.module('app', [])
.controller('ctrl', function($scope) {

})
.directive('dir', function($rootScope){
  return {
    require: 'ngModel',
    link: function(scope, element, attrs) {
      $rootScope.logs = [];
      scope.$watch(attrs.ngModel, function() {
        // here I need to check if the change was from the UI or from the controller
        
        $rootScope.logs.push('change');
      });
    }
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<div data-ng-app="app" data-ng-controller="ctrl">
  <input type="text" data-ng-model="model" data-dir="" />
  <button data-ng-click="model = 'asd'">Set "model" to defferent value</button>

  {{model}}
  <hr />
  <h3>console <button data-ng-click="$root.logs = []">clear console</button></h3>
  <ul>
    <li data-ng-repeat="log in $root.logs track by $index" data-ng-bind="log"></li>
  </ul>
</div>

http://jsbin.com/vufawur/edit?html,js,output

更新

示例 2:

angular.module('app', [])
.controller('ctrl', function($scope, $timeout) {
  $timeout(function() {
      $scope.model = 'asd';
  }, 3000);
})
.directive('dir', function($rootScope){
  return {
    require: 'ngModel',
    link: function(scope, element, attrs) {
      $rootScope.logs = [];
      scope.$watch(attrs.ngModel, function() {
        // here I need to check if the change was from the UI or from the controller

        $rootScope.logs.push('change');
      });
    }
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<div data-ng-app="app" data-ng-controller="ctrl">
  ...wait until data "return from the server"<br />
  <input type="text" data-ng-model="model" data-dir="" />
  
  {{model}}
  <hr />
  <h3>console <button data-ng-click="$root.logs = []">clear console</button></h3>
  <ul>
    <li data-ng-repeat="log in $root.logs track by $index" data-ng-bind="log"></li>
  </ul>
</div>

【问题讨论】:

    标签: javascript angularjs angularjs-directive angularjs-watch


    【解决方案1】:

    ext-change ng-model 的外部变更指令

    使用$viewChangeListener 保存最后的用户输入,并让监视处理程序进行比较,以区分对模型的外部更改和用户对模型的输入更改。

    .directive('extChange', function(){
      return {
        require: 'ngModel',
        link: function(scope, element, attrs, modelCtrl) {
            var lastUserInput = modelCtrl.$viewValue;
            modelCtrl.$viewChangeListeners.push(function() {
                lastUserInput = modelCtrl.$viewValue;
            });
            scope.$watch(attrs.ngModel, function watchHandler (value) {
                if (value!==lastUserInput) {
                    scope.$eval(attrs.extChange, {$value:value});
                }                
            });
        }
      }
    });
    

    示例指令保存最后的用户输入。当 watch 处理程序获得一个不同的值时,它会调用由 ext-change 属性定义的 Angular 表达式。更改的值公开为$value

    <input ng-model="someInput"
           ng-change="userInput=someInput"
           ext-change="extInput=$value">
    

    ext-change 指令与ng-model 指令配合使用,是ng-change 指令的补充。

    在此示例中,ext-change 指令仅在模型的外部更改时更新 extInput 变量。 ng-change 指令仅针对用户更改更新 userInput 变量。

    DEMO on JSFiddle


    该指令也可用于调用函数。

    <input ng-model="someInput"
           ng-change="userEvent(someInput)"
           ext-change="externalEvent($value)">
    

    【讨论】:

      【解决方案2】:

      不要使用$watch。不应该用,不能用,用$watch就麻烦了,已经麻烦了,别用了。

      使用控制流和事件。可能你已经有很多watcher和scope汤了,现在还不晚,尽快重构,对你最好。

      angular.module('app', [])
        .controller('ctrl', function($scope) {
      
        })
        .directive('dir', function($rootScope) {
          return {
            require: 'ngModel',
            link: function($scope, element, attrs) {
              $rootScope.logs = [];
      
              $scope.modelChange = function(reason) {
                $rootScope.logs.push(reason);
              };
      
              $scope.modelChangedFromInput = function(model) {
                $scope.modelChange('From input');
              };
      
              $scope.buttonClick = function() {
                $scope.model = 'asd';
                $scope.modelChange('From button');
              };
            }
          }
        });
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
      <div data-ng-app="app" data-ng-controller="ctrl">
      
        <input type="text" data-ng-model="model" data-dir="" data-ng-change="modelChangedFromInput()" />
      
        <button data-ng-click="buttonClick()">Set "model" to different value</button>
      
        {{model}}
        <hr />
        <h3>console <button data-ng-click="$root.logs = []">clear console</button>
        </h3>
        <ul>
          <li data-ng-repeat="log in $root.logs track by $index" data-ng-bind="log"></li>
        </ul>
      </div>

      【讨论】:

      • 感谢您的回答!对于我的问题,这是一个很好的解决方案,但实际情况有点复杂。模型没有被用户交互改变,而是在控制器中被从服务器返回的数据(使用 AJAX)改变所以我不能使用这个技巧。我用一个更好的例子更新了我的问题。
      猜你喜欢
      • 2011-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-09
      • 1970-01-01
      相关资源
      最近更新 更多