简体   繁体   English

Angular自定义指令-$ scope。$ watch不触发

[英]Angular custom directive - $scope.$watch not firing

I'm trying to make a dropdown directive, using the Angular UI code for inspiration. 我正在尝试使用Angular UI代码来启发下拉指令。

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. 我可以在打开和关闭下拉列表的意义上使它正常工作,但是我尝试对其进行扩展,以便“ dropdownManager”服务可以关闭页面上的所有下拉列表,以便可以使用来打开和关闭该下拉列表。页面控制器代码。

To do this I've created an attribute called "is-open" and put an angular watch on it. 为此,我创建了一个名为“ is-open”的属性,并在其上放置了一个角形手表。 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. 我知道值在变化,因为否则下拉列表将不会打开和关闭,但是手表绝对不会变硬,因为其中包含console.log。

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! 奇怪的是,由于console.log被写入,因此它在首次初始化时为页面上的每个下拉菜单触发一次!

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用户应该很容易理解。

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. 手表被调用一次的原因可能是因为isOpen从undefined变为false。 I think your problem is that isOpen is defined in different scopes. 我认为您的问题是isOpen在不同的范围内定义。

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. 我认为这是因为jQuery click事件是在“ Angular世界之外”发生的,因此将其包装在apply中会导致摘要周期发生在click之后,然后触发手表。

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. 我是说单词,但我真的不知道它们是什么意思。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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