AngularJS:没有从指令调用ngChange

时间:2017-02-24 16:56:23

标签: javascript angularjs angularjs-directive frontend

简要介绍我的问题

我有一个动态显示复选框列表的指令。它有一个名为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()函数。任何人都知道为什么这不起作用,因为我希望它能起作用?

2 个答案:

答案 0 :(得分:0)

首先,请尝试提供jsfiddle,codepen等代码段链接,以便其他人轻松回答您的问题。

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

要解决此问题,只需通过手动调用ngModel.$setViewValue()来更新模型并传入新的Object,以便引用更改并触发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>

答案 1 :(得分:0)

@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()">
    
  3. 现在,当任何复选框更改时,它将自动调用外部ng-change函数,而无需克隆到模型中的中间步骤。