Reputation: 1933
Summary :
When attempting to modify a scope variable (an array) that has been set for two way data binding within an isolate scoped directive, the modifications to the variable within the isolated scope directive do not reflect the changes within the controller's scope.
Toy Example :
Currently I have a sorting directive that I want to define on a table that is using ng-repeat. ng-repeat is using the same data in the ATableController
controller scope to create the table:
$scope.tableData = [{'col1' : 1,'col2' : 'fff'},
{'col1' : 2,'col2' : 'aaa'},
{'col1' : 3,'col2' : 'bbb'},
{'col1' : 4,'col2' : 'ccc'}];
I want the sorting directive to manipulate the same data from within its isolated scope.
Set up in the directive as the following
... scope :
{
tableSortData : '=',
tableSortRowAccessor : '=',
tableSortPrimer : '='
},
...
Referred to in index as the following
...
<body ng-controller="ATableController">
<table table-sort table-sort-data="tableData">
...
Within the link function a click handler is assigned to the table header. On click this handler fires off the sort
function which eventually runs this line of code.
scope.tableSortData = []; //which would be a sorting line in the real code
These changes are NOT made from within the controller and thus the table doesn't change as expected.
Is this a vanilla js problem, (wrong references are being made, etc...) or is it something with my angular directive. Either way, what am I doing wrong and how can this be fixed?
The Code :
plunkr
http://plnkr.co/edit/5yxRj6?p=info
index.html
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script data-require="angular.js@*" data-semver="1.2.0" src="http://code.angularjs.org/1.2.0/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="ATableController">
<table table-sort table-sort-data="tableData">
<thead>
<tr>
<th>Column1</th>
<th>Column2</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in tableData" >
<td>{{row.col1}}</td>
<td>{{row.col2}}</td>
</tr>
</tbody>
</table>
</body>
</html>
script.js
var myApp = angular.module('myApp', [])
.controller('ATableController', function($scope)
{
$scope.tableData = [{'col1' : 1,'col2' : 'fff'},
{'col1' : 2,'col2' : 'aaa'},
{'col1' : 3,'col2' : 'bbb'},
{'col1' : 4,'col2' : 'ccc'}];
})
.directive('tableSort', [function()
{
return {
restrict : 'A',
replace : false,
scope :
{
tableSortData : '=',
tableSortRowAccessor : '=',
tableSortPrimer : '='
},
link : function(scope, element)
{
console.log(scope.tableSortData);
// Await click events on header
element.find('th').on('click.header', function(event)
{
var field = $(event.target).attr('field-name');
augmentTableSortData();
});
function augmentTableSortData()
{
console.log(scope);
console.log(scope.tableSortData);
scope.tableSortData = [];
console.log(scope.tableSortData);
}
}
}
}])
Upvotes: 1
Views: 1908
Reputation: 18339
The issue is that you are not calling scope.apply in your click function:
function augmentTableSortData()
{
console.log(scope);
console.log(scope.tableSortData);
scope.tableSortData = [];
console.log(scope.tableSortData);
scope.$apply(); // <------ this needs to be called because the scope data is being modified in the click function outside of angular's digest cycle.
}
Demo: http://plnkr.co/edit/as5Wek?p=preview
Scopes provide APIs ($apply) to propagate any model changes through the system into the view from outside of the "Angular realm" (controllers, services, Angular event handlers).
Upvotes: 4