最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Angular directive, binding click event to outside element not working - Stack Overflow

programmeradmin0浏览0评论

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/ponents/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 ?

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/ponents/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 ?

Share Improve this question asked Oct 20, 2014 at 10:57 BrenBren 2,2141 gold badge28 silver badges46 bronze badges 3
  • Try calling scope.$apply() after calling scope.toggle() in the event listener. – Nikos Paraskevopoulos Commented Oct 20, 2014 at 11:08
  • @NikosParaskevopoulos Yea, it works after calling scope.$appy() . Thanks – Bren Commented Oct 20, 2014 at 12:13
  • @NikosParaskevopoulos Can you post that as an answer ? – Bren Commented Oct 20, 2014 at 12:27
Add a ment  | 

2 Answers 2

Reset to default 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.

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;
    });
}
发布评论

评论列表(0)

  1. 暂无评论