Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bind events on AngularJS element directives using jQuery

I have a directive in AngularJS:

module = angular.module("demoApp", [], null);
module.directive('sample', function () {
    return {
        restrict: "E",
        transclude: true,
        replace: true,
        template: "<div ng-transclude></div>",
        controller: function ($scope, $element) {
            this.act = function (something) {
                //problematic line HERE
                $element.trigger("myEvent.sample", [something]);
            };
        }
    };
})
.directive('item', function () {
    return {
        restrict: "E",
        require: "^sample",
        transclude: true,
        replace: true,
        template: "<a ng-transclude style='display: inline-block; border: 1px solid crimson; margin: 2px; padding: 5px; color: crimson; text-decoration: none; background: #f5d0d0; cursor: pointer;'></a>",
        link: function (scope, element, attributes, parentController) {
            element.on("click", function () {
                parentController.act(this.innerText);
            });
        }
    }
});

And in my HTML I use it thus:

<sample id="myElement">
    <item>1</item>
    <item>2</item>
</sample>

Which will be rendered as:

<div ng-transclude="" id="myElement">
    <a ng-transclude="" style="display: inline-block; border: 1px solid crimson; margin: 2px; padding: 5px; color: crimson; text-decoration: none; background: #f5d0d0; cursor: pointer;;display: inline-block; border: 1px solid crimson; margin: 2px; padding: 5px; color: crimson; text-decoration: none; background: #f5d0d0; cursor: pointer;" class="ng-scope"><span class="ng-scope">1</span></a>
    <a ng-transclude="" style="display: inline-block; border: 1px solid crimson; margin: 2px; padding: 5px; color: crimson; text-decoration: none; background: #f5d0d0; cursor: pointer;;display: inline-block; border: 1px solid crimson; margin: 2px; padding: 5px; color: crimson; text-decoration: none; background: #f5d0d0; cursor: pointer;" class="ng-scope"><span class="ng-scope">2</span></a>
</div>

I want to be able to listen to events triggered manually through jQuery:

$("#myElement").on("myEvent.sample", function (e, something) {
    alert("click: " + something);
});

I want this event to be triggered whenever the link is clicked.

If I set the replace property to false on the sample directive, it works. I guess it is because at the point where the event is fired, the element sample no longer exists, and as such it will have been replaced by the inner template.

So, how do I accomplish this?

Doing this, as suggested in the answer below, will not accomplish my purpose:

$($element).trigger("myEvent.sample", [something]);
like image 289
Milad Naseri Avatar asked Feb 21 '14 08:02

Milad Naseri


2 Answers

Please find below the fiddle

fiddle

Trigger is a jquery function which will work on proper handler.

$(element).trigger("myEvent.sample");

Hope this helps

like image 88
Sai Avatar answered Sep 24 '22 08:09

Sai


Here's updated fiddle: http://jsfiddle.net/3u3zh/1/

There are several points worth noticing:

  1. Because of the way angular transforms DOM, I would advice attaching all your custom listeners on body and then filter them by event target. $('body').on('myEvent.sample', 'target-selector-expr', handler) does exactly that. For example, if you use custom event listeners on ngRepeated elements, your handlers would not be executed because those elements would not exist at the time of trying to attach events to them.

  2. It seems that angular's jqLite implementation is somewhat lacking in features when triggering events. Therefore I wrapped sample's element in jQuery ($($element)) because otherwise the additional data would not get to the handler.

Final template:

<div ng-app="myApp">
  <sample id="myElement"><item>1</item><item>2</item></sample>
</div>

Js:

var myApp=angular.module('myApp',[]);
myApp.directive('sample', function () {
    return {
        restrict: "E",
        replace: true,
        transclude: true,
        template: "<div ng-transclude></div>",
        controller: function ($scope, $element) {
            this.act = function (something) {
               $($element).trigger("myEvent.sample", [something]);
            };
        }
    };
})
.directive('item', function () {
    return {
        restrict: "E",
        require: "^sample",
        transclude: true,
        template: "<a ng-transclude></a>",
        link: function (scope, element, attributes, parentController) {
            element.on("click", function(e) {
                parentController.act(this.innerHTML);
            });
        }
    }
})

$(document).ready(function(){
    $("body").on("myEvent.sample", '#myElement', function (e, something) {
        alert('clicked: ' + something);
    });
});
like image 33
package Avatar answered Sep 23 '22 08:09

package