AngularJS:ngChange 不是从指令调用的



简要介绍我的问题

我有一个动态显示复选框列表的指令。它有一个名为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的选项)被取消选中。

我的指令

模板.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>

指令.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()函数。任何人都知道为什么这不起作用,因为我希望它有效?

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

您在这种情况下的问题是,在传递对象的引用时,您永远不会更新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>

@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函数,而无需克隆到模型中的中间步骤。

最新更新