In my directive, I have a controller variable, page which gets incremented when you press the button in the directive. However, the next line, scope.alertPage() which calls the controller function does not reflect this change. Notice, when you click the button page is still alerted as 1!
I know I can fix this by adding $scope.$apply in the controller but then I get the error that says a digest is already taking place.
Plunker
app = angular.module('app', []);
app.controller('myCtrl', function($scope) {
$scope.page = 1;
$scope.alertPage = function() {
alert($scope.page);
}
})
app.directive('incrementer', function() {
return {
scope: {
page: '=',
alertPage: '&'
},
template: '<button ng-click="incrementPage()">increment page</button>',
link: function(scope, elem, attrs) {
scope.incrementPage = function() {
scope.page += 1;
scope.alertPage();
}
}
}
})
html template:
<body ng-app='app' ng-controller='myCtrl'>
page is {{page}}
<incrementer page='page' alert-page='alertPage()'></incrementer>
</body>
The reason why it does not show the updated value immediately is because the 2 way binding updates the parent (or the consumer scope of the directive) scope's bound value only during the digest cycle. Digest cycle happens after the ng-click is triggered. And hence $scope.page in the controller is not yet updated. You can get around this in many ways by using a timeout which will defer the action to run at the end of the digest cycle. You could also do it by setting an object which holds the value as 2-way bound property. Since 2-way bound property and parent scope share the same object reference you will see the change immediately.
Method 1 - using a timeout:
scope.incrementPage = function() {
scope.page += 1;
$timeout(scope.alertPage)
}
Method 2 - Bind an object:
//In your controller
$scope.page2 = {value:1};
//In your directive
scope.incrementPage = function() {
scope.page.value += 1;
scope.alertPage();
}
Method3 - Pass the value using function binding with argument:
//In your controller
$scope.alertPage = function(val) {
alert(val);
}
and
<!--In the view-->
<div incrementer page="page" alert-page="alertPage(page)"></div>
and
//In the directive
scope.incrementPage = function() {
scope.page += 1;
scope.alertPage({page:scope.page});
}
app = angular.module('app', []);
app.controller('myCtrl', function($scope) {
$scope.page = 1;
$scope.page2 = {value:1};
$scope.alertPage = function() {
alert($scope.page);
}
$scope.alertPage2 = function() {
alert($scope.page2.value);
}
})
app.directive('incrementer', function($timeout) {
return {
scope: {
page: '=',
alertPage: '&',
page2:"=",
alertPage2: '&'
},
template: '<button ng-click="incrementPage()">increment page</button>',
link: function(scope, elem, attrs) {
scope.incrementPage = function() {
scope.page += 1;
scope.page2.value += 1;
$timeout(function(){ scope.alertPage() });
scope.alertPage2();
}
}
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.5/angular.min.js"></script>
<div ng-app="app" ng-controller="myCtrl">
<div incrementer page="page" alert-page="alertPage()" page2="page2" alert-page2="alertPage2()"></div>
</div>
You can pass the variable by reference, then the update will be immediate (because you wont copy it, but simply pass its location in memory).
View:
<incrementer page="data" alert-page='alertPage()'></incrementer>
Directive:
link: function(scope, elem, attrs) {
scope.incrementPage = function() {
scope.page.page += 1;
scope.alertPage();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With