Angular directive, binding click event to outside element not working

馋奶兔 提交于 2019-12-11 22:36:45

问题


I am trying to create a custom event for toggling with Angular. The directive is called toggleable.

It sounds trivial at first, but tricky bit is that i want to be able to to use any button or link on the page for toggling.

The code i have written is as below

Directive

'use strict';

angular.module('onsComponents')
.directive('togglable', function() {
return {
  restrict: 'A',
  transclude: true,
  scope: {
    togglerId: '@',
    show: '@'
  },
  link: function(scope, element, attrs) {
    if (!scope.togglerId) {
      console.error("Toggler id must be given")
      return
    }

    scope.visible = (scope.show === 'true')
    scope.toggle = function() {
      alert("Before " + scope.visible)
      scope.visible = !scope.visible
      alert("After " + scope.visible)
    }

    //Bind click event to given dom element
    angular.element('#' + scope.togglerId).bind("click", function() {
      scope.toggle()
    })


  },
  templateUrl: 'app/components/toggler/toggler.html'
  }
  })

The Template

<div ng-transclude ng-show="visible">

</div>

The View

<a href="" id="toggleButton" >Click me to toggle</a>
<div togglable toggler-id="toggleButton">
  Hey
</div>

The toggle link seems to be working. I can see the alerts when the link is clicked. The trouble is the content does not appear. It seems like the link is not really is in the same scope with the content, another scope is created when i do this.

If i move the click link within the template as below, it works. But that is not really what i want.

<!-- Move the link inside template -->
<a href=""  ng-click="toggle()" >Click me to toggle</a>
<div ng-transclude ng-show="visible">

</div>

So how can i achieve that ?


回答1:


You have to call scope.$apply() every time you are changing stuff that Angular watches outside of Angular. By outside I mean events bound using the jQuery API - or any other way besides Angular's native ng-click etc.

So do either:

scope.toggle = function() {
    scope.visible = !scope.visible;
    scope.$apply();
}

Or:

scope.toggle = function() {
    scope.$apply(function() {
        scope.visible = !scope.visible;
    });
}



回答2:


It is more angular-esque to create two directives and a service that links them for this purpose. As a rough outline:

app.service('toggleService', function() {
    var toggleables = {};

    this.registerToggleable = function(key, f) {
        toggleables[key] = f;
    }

    this.toggle = function(key) {
        var f = toggleables[key];
        f();
    }
});

app.directive('toggler', function(toggleService) {
    return {
        link: function(scope, elem, attrs) {
            elem.bind("click", function() {
                toggleService.toggle(attrs.toggler);
                // because the event handler operates outside of angular digest cycle
                scope.$apply();
            });
        }
    }
})

app.directive('toggleable', function(toggleService) {
    return {
        link: function(scope, elem, attrs) {
            function toggle() {
                scope.visible = !scope.visible;
            }

            toggleService.registerToggleable(attrs.toggleable, toggle);
        }
    }
});

The above will need tweaking to actually do anything apart from set a variable on the scope, but you can then use the two like so:

<a href="" toggler="foo" >Click me to toggle</a>
<div toggleable="foo">
  Hey
</div>

This way you are declaring the toggle functionality on the elements themselves, with no ID-based lookup magic.



来源:https://stackoverflow.com/questions/26463972/angular-directive-binding-click-event-to-outside-element-not-working

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!