简体   繁体   中英

Angular custom directive - $scope.$watch not firing

I'm trying to make a dropdown directive, using the Angular UI code for inspiration.

I can get it working in the sense that it's opening and closing the dropdown, but I've tried to extend it so that a "dropdownManager" service can close all the dropdowns on the page and so that it can be opened and closed using the page controller code.

To do this I've created an attribute called "is-open" and put an angular watch on it. Unfortunately this watch isn't firing when the attribute is updated by the controller, or when the scope variable is updated from the directive's own open/close functions. I know that the value is changing because otherwise the dropdown wouldn't open and close, but the watch definitely isn't foring because I have a console.log inside it.

The strange thing is that it does fire once for each dropdown on the page when it first initializes because the console.log gets written!

Anyway, I tride to make a fiddle but there are too many dependencies, sorry, but I have managed to reduce the code down as much as possible so hopefully it should be very easy for a seasoned Angular user to understand.

angular.module('directives').directive('dropdown', function () {

    return {

        restrict: 'EA',
        scope: {
            isOpen: '=?',
        },

        link: function ($scope, $element, $attrs, ngModel) {


            // Defaults
            $scope.isOpen = false;

            // Watch the isOpen variable
            $scope.$watch('isOpen', function(value) {

                console.log("Scope Changed", $scope.isOpen);

                // Open or close this panel
                if (value === true){
                    $scope.openDropdown();
                }
                else{
                    $scope.closeDropdown();
                }

            });            


            // Open Dropdown
            $scope.openDropdown = function($event){

                $scope.isOpen = true;

                var panelEl = $element.find('[dropdown-panel]');

                panelEl.slideDown(100, function(){
                    $element.addClass('is-open');
                });

            };



            // Open Dropdown
            $scope.closeDropdown = function($event){

                $scope.isOpen = false;

                var panelEl = $element.find('[dropdown-panel]');

                $element.removeClass('is-open');

                panelEl.slideUp(100);

            };


            // Toggle Dropdown
            $scope.toggleDropdown = function ($event) {

                if(!$scope.isOpen){
                    $scope.openDropdown();
                }

                else{
                    $scope.closeDropdown();
                }

            };


            // Add click event to toggle element
            $element.find('[dropdown-toggle]').bind('click', $scope.toggleDropdown);


        }        

    };

});

A good option to close and open all your dropdowns at once is to use events :

$scope.$on('close all modals', function(){
  $scope.isOpen = false;
})

$scope.$on('open all modals', function(){
  $scope.isOpen = true;
})

and then when you want to close everything, wherever in your code you can call :

$rootScope.$broadcast('close all modals');

The reason why your watch is called once is probably because isOpen changes from undefined to false. I think your problem is that isOpen is defined in different scopes.

OK, so I found the answer myself in the end.

This:

$element.find('[dropdown-toggle]').bind('click', $scope.toggleDropdown);

Needed to become:

$element.find('[dropdown-toggle]').bind('click', function(){
    $scope.$apply($scope.toggleDropdown);
});

I assume this is because the jQuery click event is happening "outside the Angular world" so wrapping it in an apply causes the digest cycle to happen after the click, which then fires the watch.

Don't take my word on any of that though. I'm saying words but I don't really have any idea what they mean.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM