12

In my DOM in AngularJS, I am using an ng-include inside a ng-repeat directive. It is loading the HTML just fine. Anyway, an issue I have is that I'm using JQuery (latest version) to bind a few mouse over and mouse click events on elements in the DOM with the same class as those added to the DOM by the ng-repeat and include. JQuery doesn't seem to apply the event handlers to the new DOM addition, though.

In previous versions .live() seemed to do what I want, but it has been removed in the latest version. Should I clear the bindings on the elements and re-create them every time the DOM has additional elements of the class added?

Elnur Abdurrakhimov
  • 44,533
  • 10
  • 148
  • 133
Christian Stewart
  • 15,217
  • 20
  • 82
  • 139

3 Answers3

31

Answer here:

This is a multifaceted question. First of all, all jQuery code events should use $scope.$apply to execute angularjs code as expected. Example:

jQuery(selector).someEvent(function(){
    $scope.$apply(function(){
      // perform any model changes or method invocations here on angular app.
    });
});

In the latest version of jQuery, in order to have events that update based on DOM changes, you want to 1. Get a selector for the nearest parent element (ex):

<div class="myDiv">
<div class="myDynamicDiv">
</div>
<!-- Expect that more "myDynamicDiv" will be added here -->
</div>

In this case, your parent selector would be ".myDiv", and your child selector would be .myDynamicDiv.

Thus, you want jQuery to have the events on the PARENT of the element so if the child elements change, it will still work:

$('.myDiv').on("click", ".myDynamicDiv", function(e){
      $(this).slideToggle(500);
      $scope.$apply(function(){
           $scope.myAngularVariable = !$scope.myAngularVariable;
      });
});

Notice that the $scope.$apply() is used in order to enable jQuery to access those angular variables.

Hope this helps! Christian

Christian Stewart
  • 15,217
  • 20
  • 82
  • 139
  • Did you mean $apply everywhere instead of $run? Also, a more Angular way to do this would be to write a directive that handles the animation. The directive's link function could bind to the click event, or ng-click could be used in the template. The click event handler would be a $scope function (defined in the directive's controller or link function) that performed the animation and $scope property change. – Mark Rajcok Feb 04 '13 at 21:10
  • Right, you could have a directive, but if you want to use jQuery with dynamically updating DOM elements (which is really what this question is about) this is how you do it. If you're also using angular you can use $scope.$run to access angular stuff from within jQuery. – Christian Stewart Feb 04 '13 at 21:13
  • @MarkRajcok It is $scope.$apply, by the way – Christian Stewart Jun 25 '13 at 22:41
  • Thanks for the tip about binding events to the parent to watch for child element changes! It helped in my situation, where the older version of the app used raw jQuery to bind, and now we're gradually introducing angular to render the pages. The jQuery bindings were happening *before* angular rendered the view, so nothing was happening. – cloudberry Apr 22 '15 at 08:46
2

disclaimer: I have been learning angular myself so these are basically just half-educated guesses.

First, consider using angular directives for the job as stated by Bertrand and Mark.

Second, make sure that jquery gets loaded before angular, see here.

Third, it might be a (dynamic) binding issue, see here.

Please, feedback.

Community
  • 1
  • 1
linski
  • 5,046
  • 3
  • 22
  • 35
0

This question has already been nicely answered by Christian Stewart, but I just wanted to add something to his response.

I use the following code, and add the "cssClickableIcon" class to my buttons, so that when the user clicks on a button (or a div control), it shrinks inwards slightly.

enter image description here

It's a little tweak, but has a really nice effect on a webpage. You really feel as though the webpage is more interactive, rather than flat, and that you have clicked on the control.

//  Make the buttons "bounce" when they're clicked on
$('body').on("mousedown", ".cssClickableIcon", function (e) {
    $(this).css("transform", "scale(0.9)"); 
});
$('body').on("mouseup", ".cssClickableIcon", function (e) {
    $(this).css("transform", "");
});

Because this uses jQuery's on function, this does work with buttons or other DOM elements created by an AngularJS ng-repeat command.

And, of course, if you're using Bootstrap's own button CSS classes, you can easily add this effect across all of your Bootstrap/Angular buttons using this:

$('body').on("mousedown", ".btn", function (e) {
    $(this).css("transform", "scale(0.9)"); 
});
$('body').on("mouseup", ".btn", function (e) {
    $(this).css("transform", "");
});

I love this effect so much, and it's such a simple thing to add !

Update

Christian Stewart suggested using CSS instead of jQuery, and he's absolutely right...

So, you can achieve the same effect using just this simple CSS:

.btn:active {
    transform: scale(0.9);
}
Mike Gledhill
  • 27,846
  • 7
  • 149
  • 159
  • 1
    You can just do this with CSS selectors, rather than slow JavaScript. https://developer.mozilla.org/en-US/docs/Web/CSS/:active – Christian Stewart Feb 05 '17 at 21:36
  • Excellent tip ! I didn't realise ":active" would have the same effect (and still work with Angular-created DOM elements). I've updated my article to show how to do this. Many thanks !! – Mike Gledhill Feb 06 '17 at 08:25
  • Np! You'll find that any CSS works with any dom elements no matter how they're created :) – Christian Stewart Feb 06 '17 at 22:24