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

admin2025-04-20  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;
    });
}
转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1745158051a287976.html

最新回复(0)