Matt York
Matt York

Reputation: 16261

Reuse angular directives within other directives

I would like to lazy load images and iframes (i.e. set the src once the element is in the viewport). I have an existing angular directive is-visible that sets the is-visible attribute when an element scrolls into view. How do I reuse this directive from within my set-src-when-visible directive?

i.e., I want this code to reuse the is-visible directive:

<img set-src-when-visible="http://example.com/img.png" />`
<iframe set-src-when-visible="http://example.com/" /> 

Unsatisfactory approach 1: Require is-visible directive

I can require the is-visible directive to be on the same element I am declaring the set-src-when-visible directive on:

<img set-src-when-visible="url" is-visible />

myApp.directive('setSrcWhenVisible', function () {
    return {
        restrict: 'A',
        require: 'isVisible',
        link: function ($scope, element, attrs) {
            var set = false;
            attrs.$observe('isVisible', function(isVisible) {
                if(isVisible && !set) {
                    attrs.$set('src', $scope.$eval(attrs.setSrcWhenVisible));
                    set = true;
                }
            });
        }
    }
});

Here is a working example: http://jsfiddle.net/pvtpenguin/6tCk6/

Downside: I'd like to be able to specify only the set-src-when-visible directive while still reusing is-visible.


Unsatisfactory approach 2:

Create two directives, one for iframe and one for img tag:

<iframe set-iframe-src-when-visible="url"></iframe>
<img set-img-src-when-visible="url" />

angular.forEach(['Iframe', 'Img'], function(tagName) {
    var directiveName = 'set' + tagName + 'SrcWhenVisible';
    myApp.directive(directiveName, function ($window) {
        return {
            restrict: 'A',
            template: '<' + tagName + ' is-visible></' + tagName + '>',
            replace: true,
            link: function ($scope, element, attrs) {
                var set = false;
                attrs.$observe('isVisible', function(value) {
                    if(value && !set) {
                        attrs.$set('src', $scope.$eval(attrs[directiveName]));
                        set = true;
                    }
                });
            }
        }
    });
});

Working example: http://jsfiddle.net/pvtpenguin/K4JuC/2/

Downside: different names for each directive, plus this doesn't fit well with the method I am using to declare and configure my app.

Upvotes: 1

Views: 571

Answers (1)

Mark Rajcok
Mark Rajcok

Reputation: 364677

Based on our comment discussion, here is a link function that hopefully will do what you want:

link: function ($scope, element, attrs) {
   $scope.src = attrs.setSrcWhenVisible;  // save attribute -- is $eval needed?
   var tagName = element[0].tagName;
   if(tagName == 'IMG') {
      var jqLiteWrappedElement = angular.element('<img is-visible></img>');
   } else {
      var jqLiteWrappedElement = angular.element('<iframe is-visible></iframe>');
   }
   element.replaceWith(jqLiteWrappedElement);
   $compile(jqLiteWrappedElement)($scope);
   var set = false;
   attrs.$observe('isVisible', function(value) {
      if(value && !set) {
         attrs.$set('src', $scope.src);
         set = true;
      }
   });
}

Fiddle

Upvotes: 1

Related Questions