0

I'm attempting to bind an event handler to elements that don't yet exist within the DOM but am running into issues. I think where the problem begins is at line #51, where I attempt to find an element that doesn't exist (because ng-repeat hasn't yet generated it). Is there a way around this without creating a binding that is too general (e.g., $("a").on("click", function () {});)?

cover-photo-carousel.js

  1 var MyApp = angular.module("MyApp");
  2
  3 MyApp.directive("coverPhotoCarousel", ["$compile", function ($compile) {
  4   return {
  5     restrict: "EA",
  6     replace: true,
  7     templateUrl: "cover-photo-carousel.html",
  8     scope: true,
  9     controller: function ($scope) {
 10       $scope.images = [
 11         {
 12           title: "",
 13           url: "http://blah.com/abc.jpg",
 14           description: "",
 15           cover: true
 16         },
 17         {
 18           title: "",
 19           url: "http://blah.com/abc.jpg",
 20           description: ""
 21         },
 22         {
 23           title: "",
 24           url: "http://blah.com/abc.jpg",
 25           description: ""
 26         },
 27         {
 28           title: "",
 29           url: "http://blah.com/abc.jpg",
 30           description: ""
 31         }
 32       ];
 33       $scope.bark = function () {
 34         console.log("Something.");
 35       };
 36     },
 37     link: function (scope, element, attributes) {
 38
 39       if (scope.images.length > 1) {
 40         element.find(".cover-photo-control .glyphicon").show();
 41       }
 42
 43       scope.images.forEach(function (value, index) {
 44         if (value.cover) {
 45           element.css({
 46             "background-image": "url(" + value.url + ")"
 47           });
 48         }
 49       });
 50
 51       element.find(".thumbnails-container .thumbnails ul li a").on("click", function (e) {
 52         e.preventDefault();
 53         element.css({
 54           "background-image": "url(" + scope.images[1].url + ")"
 55         });
 56       });
 57     }
 58   }
 59 }]);

cover-photo-carousel.html

<div class="cover-photo-carousel">
  <div class="cover-photo-control back text-center">
    <span class="glyphicon glyphicon-chevron-left"></span>
  </div>
  <div class="cover-photo-control forward text-center">
    <span class="glyphicon glyphicon-chevron-right"></span>
  </div>
  <div class="thumbnails-container">
    <div class="thumbnails">
      <ul class="clearfix">
        <li ng-repeat="image in images"> 
          <a href="#"><img src="{{image.url}}" alt="" width="90"  /></a>
        </li>
      </ul> 
    </div>
  </div>
</div>
2
  • 1
    try delegated handler element.on("click", '.thumbnails-container .thumbnails ul li a', function (e) {}); Commented Nov 24, 2014 at 13:09
  • 1
    don't use jQuery approach when angular already has ng-click. If you change scope values within external event handler you also have to tell angular about them Commented Nov 24, 2014 at 13:16

2 Answers 2

1

The angular way could be something like

var app = angular.module('my-app', [], function() {

})

app.controller('AppController', function($scope) {

  $scope.message = "Welcome";
})
app.directive("coverPhotoCarousel", ["$compile",
  function($compile) {
    return {
      restrict: "EA",
      replace: true,
      templateUrl: "cover-photo-carousel.html",
      scope: true,
      controller: function($scope) {
        $scope.images = [{
          title: "",
          url: "http://placehold.it/90&text=abc",
          description: "",
          cover: true
        }, {
          title: "",
          url: "http://placehold.it/90&text=def",
          description: ""
        }, {
          title: "",
          url: "http://placehold.it/90&text=ghi",
          description: ""
        }, {
          title: "",
          url: "http://placehold.it/90&text=jkl",
          description: ""
        }];
        $scope.bark = function() {
          console.log("Something.");
        };
        $scope.myStyle = {};
        $scope.clicked = function(image) {
          $scope.myStyle['background-image'] = 'url(' + image.url + ')'
        }
      },
      link: function(scope, element, attributes) {

        if (scope.images.length > 1) {
          element.find(".cover-photo-control .glyphicon").show();
        }

        scope.images.forEach(function(value, index) {
          if (value.cover) {
            element.css({
              "background-image": "url(" + value.url + ")"
            });
          }
        });

        element.find(".thumbnails-container .thumbnails ul li a").on("click", function(e) {
          e.preventDefault();
          element.css({
            "background-image": "url(" + scope.images[1].url + ")"
          });
        });
      }
    }
  }
]);
.cover-photo-carousel {
  background-repeat: no-repeat;
  background-position: center center;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="my-app" ng-controller="AppController">

  <cover-photo-carousel></cover-photo-carousel>
  <script type="text/ng-template" id="cover-photo-carousel.html">
    <div class="cover-photo-carousel" ng-style="myStyle">
      <div class="cover-photo-control back text-center">
        <span class="glyphicon glyphicon-chevron-left"></span>
      </div>
      <div class="cover-photo-control forward text-center">
        <span class="glyphicon glyphicon-chevron-right"></span>
      </div>
      <div class="thumbnails-container">
        <div class="thumbnails">
          <ul class="clearfix">
            <li ng-repeat="image in images">
              <a href="#" ng-click="clicked(image)">
                <img src="{{image.url}}" alt="" width="90" />
              </a>
            </li>
          </ul>
        </div>
      </div>
    </div>
  </script>
</div>

Sign up to request clarification or add additional context in comments.

2 Comments

But what if I need to manipulate the DOM from that event handler? In that case, I think the DOM-manipulating code is supposed to be located within the directive's link function. I'm not sure how to associate my controller's code with DOM-manipulating code.
@MichaelP. It depends on what kind manipulation you want to do... if it is something like show/hide or changing class/style which is supported by angularjs using directives then you can use the approach shown in the above example
0

Use ng-click to execute click handlers in AngularJS.

2 Comments

What if I need the click's event handler to manipulate the DOM? Where is that DOM-manipulating logic supposed to go? I thought ng-click mapped to properties and methods on the directive's controller and that DOM-manipulating logic isn't supposed to be stored within a controller but rather within a link function.
You should try to avoid manual DOM manipulation alltogether in AngularJS. There's plethora of tools like ng-show, ng-if, ng-switch, ng-include, etc. to let your templates adjust themselves dynamically for you. See "Thinking in AngularJS".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.