Reputation: 429
I am a little bit confused about when to use $apply in Angularjs, my confusion come from the following example.
Here is my design:
1) I make a custom directive "profile-modal" which has the attribute "visible", the "visible" attribute stores a "profileShow" variable which decide whether or not to show the popup box
2) In the directive, I two way bind the visible attrible as the outer scope controller will modify the "profileShow" variable and at the same time the directive will modify the "visible" attribute to show/hide the box
3) When the user click the designated button, the controller will modify the "profileShow" vaiable, box will popup
4) When the user close the bootstrap box, the on hidden function will change the attribute "visible" which trigger the $watch in directive to close the dialog.
Here is the code
<profile-modal visible="profileShow"></profile-modal>
<button type="button" class="btn btn-primary btn-sm" ng-click="addConfProfile()">
<span class="glyphicon glyphicon-plus"></span> Add Profile
</button>
define(['testConfig','bootstrap-dialog'],function(testConfig, BootstrapDialog){
testConfig
.controller('apConfCtrl', function($scope){
$scope.profileShow = false;
$scope.addConfProfile = function(){
$scope.profileShow = !$scope.profileShow;
};
})
.directive('profileModal',function(){
return {
restrict: 'E',
templateUrl: 'templates/profile_add_dialog.html',
replace: true,//replace the original profile modal dom
scope:{
visible: '=',
},
link: function(scope, elm, attr){
//set modal title
scope.profileTitle = "Add Profile";
scope.$watch('visible', function(n,o){
console.log(n);
console.log(o);
if (n){
$(elm).modal('show');
}
else{
$(elm).modal('hide');
}
});
$(elm).on('hidden.bs.modal', function(){
scope.visible = false;
scope.$apply(); // >>Why this is necessary??
//console.log(scope.visible);
});
}
};
});
});
But when implemented, I face the following problem, for the FIRST time I click the button, everything works as expected, box pop up, then I close the box. In the SECOND time I try to open the box, nothing popup... Then I try to click once more, it pop ups again.
Then I figure out the main problem is that the on hidden action does not trigger the $watch function to change the attribute "visible", but the amazing part is when I try to add scope.$apply after changing the attribute variable, it works! I just want to know why I have to do this, is Angularjs suppose to help monitor the variable "visible" without calling $apply?
Appreciate for any help
Upvotes: 1
Views: 3012
Reputation: 804
It is also suggested that you define your boolean, numbers or string properties (in your case 'profileShow' ) inside an child object of $scope
instead of directly making them properties of $scope
. The reason for this approach is bool, numbers and string are passed by value while arrays and objects are passed by reference. So, angular will not call the $digest
cycle for you even if your property changes and you have to call the $apply()
method manually.
Upvotes: 0
Reputation: 38103
Angular relies on a thing called the "digest cycle" to pick up any changes. The thing that triggers that digest cycle is calling $scope.$digest()
or $scope.$apply()
.
For most of the built-in Angular directives, they already trigger a digest cycle for you. A watch listener function, for example, that gets executed if the watched expression changes, is executed during the digest cycle when a change is detected.
However, for anything not part of Angular, such as a jQuery event occurring, you need to "tell" Angular about it. Which means calling $scope.$apply()
yourself.
Upvotes: 4