【问题标题】:AngularJS: ngChange not called from directiveAngularJS:未从指令调用 ngChange
【发布时间】:2017-07-15 14:43:17
【问题描述】:

我的问题简介

我有一个动态显示复选框列表的指令。它有一个名为options 的参数,它应该是一个如下所示的数组,以便正确显示复选框列表。例如:

var options = [
    {
        id: 1,
        label: 'option #1'
    },
    {
        id: 2,
        label: 'option #2'
    },
    {
        id: 3,
        label: 'option #3'
    }
];

因此,通过将此数组传递给我的指令,将显示一组三个复选框。

此外,该指令需要ngModel,它将具有选中/取消选中复选框的结果(此对象始终通过初始化)。例如:

var result = {
    "1": true,
    "2": true,
    "3": false
};

这种情况意味着第一个和第二个复选框(带有id=1id=2 的选项)被选中,而第三个(带有id=3 的选项)未被选中。

我的指令

template.html

<div ng-repeat="option in options track by $index">
    <div class="checkbox">
        <label>
            <input type="checkbox"
                   ng-model="result[option.id]">
                {{ ::option.label }}
        </label>
    </div>
</div>

directive.js

angular
    .module('myApp')
    .directive('myDirective', myDirective);

function myDirective() {

    var directive = {
        templateUrl: 'template.html',
        restrict: 'E',
        require: 'ngModel',
        scope: {
            options: '='
        },
        link: linkFunc
    };

    return directive;

    function linkFunc(scope, element, attrs, ngModel) {
        scope.result;

        ngModel.$render = setResult;

        function setResult() {
            scope.result = ngModel.$viewValue;
        };

    };

};

我想要达到的目标

无论我在哪里使用我的指令,我都希望能够在ngModel 更改时触发一个函数。当然,我想使用ngChange 来实现这一点。到目前为止,我有以下内容:

<my-directive
    name="myName"
    options="ctrlVM.options"
    ng-model="ctrlVM.result"
    ng-change="ctrlVM.selectionChanged()">
</my-directive>

.selectionChanged() 函数不会在模型更改时触发。任何人都知道为什么这不起作用,因为我期望它起作用?

【问题讨论】:

  • .selectionChanged() 定义在哪里?和控制器?你能提供一个小提琴吗?
  • 它在使用指令的视图的控制器中定义。但这不应该是一个问题,它总是会被定义。

标签: javascript html angularjs angularjs-directive frontend


【解决方案1】:

首先,请尽量提供jsfiddle、codepen等代码sn-p链接,方便其他人回答您的问题。

您的问题是,您在传递对象的引用时永远不会更新 ctrlVM.result 对象,即使您通过调用 ngModel.$setViewValue() 手动更新模型,该引用也永远不会改变。

要解决这个问题,只需手动调用ngModel.$setViewValue() 更新模型并传入新对象,这样引用就会发生变化,这将触发ngChange 指令逻辑。

我已经添加了执行此操作的逻辑,它将成功触发更改。看下面的代码:

angular
  .module('myApp', [])
  .directive('myDirective', myDirective)
  .controller('MyController', function($timeout) {
    var vm = this;

    vm.options = [{
      id: 1,
      label: 'option #1'
    }, {
      id: 2,
      label: 'option #2'
    }, {
      id: 3,
      label: 'option #3'
    }];

    vm.result = {
      "1": true,
      "2": true,
      "3": false
    };

    vm.selectionChanged = function() {
      vm.isChanged = true;
      $timeout(function() {
        vm.isChanged = false;
      }, 500)
    }

  });

function myDirective() {

  var directive = {
    templateUrl: 'template.html',
    restrict: 'E',
    require: 'ngModel',
    scope: {
      options: '='
    },
    link: linkFunc
  };

  return directive;

  function linkFunc(scope, element, attrs, ngModel) {
    scope.result;

    ngModel.$render = setResult;

    function setResult() {
      scope.result = ngModel.$viewValue;
    };

    scope.updateValue = function(val) {
      ngModel.$setViewValue(Object.assign({}, val))
    }

  };

};
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>

<div ng-app="myApp">

  <script type="text/ng-template" id="template.html">
    <div ng-repeat="option in options track by $index">
      <div class="checkbox">
        <label>
            <input type="checkbox"
                   ng-model="result[option.id]" ng-click="updateValue(result)">
                {{ ::option.label }}
        </label>
      </div>
    </div>
  </script>

  <div ng-controller="MyController as ctrlVM">

    <my-directive name="myName" options="ctrlVM.options" ng-model="ctrlVM.result" ng-change="ctrlVM.selectionChanged()">
    </my-directive>

    <div> Data: {{ctrlVM.result}} </div>

    <div> isChanged: {{ctrlVM.isChanged}} </div>

  </div>
</div>

【讨论】:

    【解决方案2】:

    @Gaurav 正确识别了问题(ng-change 永远不会被调用,因为对象引用没有改变)。这是一个更简单的解决方案,不需要手动克隆到控制器的模型中:

    1. ng-change 属性添加绑定:

      scope: {
          options: '=',
          ngChange: '&'  // Add this, creates binding to `ctrlVM.selectionChanged()`
      }
      
    2. 在您的复选框模板中添加ng-change

      <input type="checkbox"
                   ng-model="result[option.id]" ng-change="ngChange()">
      

    现在,当任何复选框更改时,它将自动调用外部 ng-change 函数,而无需克隆到模型中的中间步骤。

    【讨论】:

      猜你喜欢
      • 2014-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-28
      • 2017-07-13
      • 2015-03-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多