Reputation: 10363
I moved my simple paging from controller to a directive and got stuck with a strange problem. Every time I update parent scope from within the directive it only gets updated on the next change. So if ng-options are [10,20,30] and I ng-change it to 10 - nothing happens. Then I change it to 20 and it updates to previous value of 10, next change to whatever value gets the model updated to the one I picked before, and so on
I tried to use $scope.$apply but it does not help. I still get delayed updates. What am I missing? I realize that it is something to do with digest update and $scope.$apply but I cant figure out where to use it. Anything I try just does not work.
Relevant controller parts:
vm.pages = 0;
vm.articles = [];
vm.load = { page: { batch: 10, current: 1 }
, sort: { _id: -1 }
, filter: {}
};
vm.getArticles = function() {
articleS.list(vm.load, function (data){
vm.pages = data.pages;
vm.articles = data.articles;
});
}
The directive:
.directive("paging", function() {
var scope = { update: '&', current: '=', pages: '=', batch: '=' };
function link(s, e, a) {
s.options = [10,20,50,100];
s.toPage = function(p) {
switch(p) {
case "last":
if (s.current != s.pages) {
s.current = s.pages;
s.update();
}
break;
case "next":
if (s.current < s.pages) {
s.current ++;
s.update();
}
break;
case "prev":
if (s.current > 1) {
s.current --;
s.update();
}
break;
default:
s.current = 1;
s.update();
}
}
}
return {
replace: true,
scope: scope,
templateUrl: 'paging.tpl',
link: link
}
});
The directive template:
<section class='pages'>
<select
ng-model="batch"
ng-change="toPage('first')"
ng-options="value for value in options">
</select>
<div>
<button ng-click="toPage('first')"> 1 </button>
<button ng-click="toPage('prev')"> << </button>
<div>{{current}}</div>
<button ng-click="toPage('next')"> >> </button>
<button ng-click="toPage('last')"> {{pages}} </button>
</div>
</section>
Directive call:
<paging
update="vm.getArticles()"
current="vm.load.page.current"
batch="vm.load.page.batch"
pages="vm.pages">
</paging>
Upvotes: 1
Views: 848
Reputation: 10363
OK. I figured this out.
If I understood correctly, $apply will generate a already in progress
error if you run it on an isolated scope variable bound to its parent with '='. I think that '=' triggers the $digest (this is just an observation from trial and error while trying to solve this).
My problem was that s.update()
function was run before the next digest cycle. So local scope variables "did not have time" to copy to the parent scope. That's why I could only see the update at the next operation or at the next digest cycle hens always seeing previous change.
Playing with setTimeout(function(){ s.update() }, 2000)
proved it.
If I delay the execution for a couple of seconds then I see the update with correct changes. Delaying is not a solution though, slowing down application is plain stupid.
Then I found angular's $timeout(function() {})
This allows to skip the delay parameter which defaults to 0 and does not actually delay anything, but it does insure that callback runs on the next digest cycle so that the local scope variables can be updated to the parent.
if (s.current < s.pages) {
s.current ++;
$timeout(function() {
s.update();
});
}
Upvotes: 6