user2499325
user2499325

Reputation: 429

$watch not triggered when value is changed

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

Answers (2)

Himanshu Mittal
Himanshu Mittal

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

GregL
GregL

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

Related Questions