Peter Hamilton Peter Hamilton - 5 months ago 23
Javascript Question

AngularJS Directive for ElevateZoom jQuery Plugin

I'm trying to use the ElevateZoom jQuery plugin inside an angular app.

Essentially, to use ElevateZoom normally, you create an image as follows:

<img id="my-img" src="small.jpg" data-zoom-image="big.jpg" />


Then in your application JS:

$('#my-img').elevateZoom(options);


This works fine in a standard app. But when I try and do it in my AngularJS app using a directive (I followed some SO answers for getting jquery plugins into angular with directives) I just can't make it work.

Live editable demo on Plunkr: http://plnkr.co/edit/Mu4EOcGtGs7XVDDUvnnB?p=preview

My directive looks like:

app.directive('ngElevateZoom', function() {
return {
restrict: 'A',
scope: true,
compile: function(scope, element, attrs) {
$(element).elevateZoom(scope.$eval(attrs.elevateZoom));
}
};
});


And my HTML looks like this:

<img ng-elevate-zoom ng-src="{{small_image}}" data-zoom-image="{{large_image}}" />


What am I doing wrong? I've only been experimenting with Angular a few days so go easy on me ;)

Answer

Your directive:

app.directive('ngElevateZoom', function() {
  return {
    restrict: 'A',
    scope: true,
    compile: function(scope, element, attrs) {
      $(element).elevateZoom(scope.$eval(attrs.elevateZoom));
    }
  };
});

Keep in mind that compile function (tElement, tAttrs, transclude) { ... } dont have access to scope, so I guess you were trying to use the link function.

Check here

I did some changes:

HTML

<img ng-elevate-zoom
     ng-src="{{small_image}}"
     zoom-image="{{large_image}}" />

JS

app.directive('ngElevateZoom', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      element.attr('data-zoom-image',attrs.zoomImage);
      $(element).elevateZoom();
    }
  };
});

When using directly data-zoom-image='{{large_image}}', was causing that elevateZoom to try to get the value from that attribute and it was '{{large_image}}' at the time of running the plugin $(element).elevateZoom();

enter image description here

Check the updated Plunker


UPDATED

Since there could be cases when the attrs need for the plugin are delayed, you'll need to $observe the attr and only when its actually ready you call the plugin.

app.directive('ngElevateZoom', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {

      // Watch for changes on the attribute before
      // calling the function.
      attrs.$observe('zoomImage', function() {
        linkElevateZoom();
      });

      function linkElevateZoom() {
        // Check that it's not empty.
        if (!attrs.zoomImage) return;
        element.attr('data-zoom-image',attrs.zoomImage);
        $(element).elevateZoom();
      }

      linkElevateZoom();
    }
  };
});

Check the updated plunker

Optional When using this in conjunction with views, the plugin leaves behind a div layered on top of the view. Here's a directive to solve that issue.

app.directive('zoomContainer', function() {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            scope.$on('$routeChangeSuccess', function() {
                var target = element.children('div.zoomContainer').remove();
            });
        }
    };
});

This directive needs to be applied to the body element.

<body zoom-container>