AngularJS: avoid using $timeout to wait for image loading completion

1.5k Views Asked by At

I'm trying to create my own image carousel as an angularjs directive; I want to keep it as lightweight and unopinionated as possibile, so I thought I'd just create a <carousel></carousel> wrapper for a set of <img> elements, like so:

<carousel>
   <img ng-repeat="image in images" ng-src="{{image.src}}" alt=""/>
</carousel>

What the directive does is to simply create a <div class="carousel"> element into which the images are transcluded. Now, I still haven't coded the part where images slide or fade in/out cause there's one issues I'd like to get out of the way first: I want to assign the carousel and all the images therein the same height (computed as the height of the shortest image) so as to avoid the carousel from changing height when a taller image gets displayed, or avoid cropping the image in case the carousel had a fixed height.

So I jotted down this JSFiddle to demonstrate, but so far the best solution I found to compute the heights of the transcluded images relies on two nested $timeouts with a 100-ms delay. It looks to me more like a hack than anything.
So I was wondering if there's a "proper" way to accomplish it in angularjs. Cheers.

P.S. On a side note, I also dislike fetching the root element of the directive's template, the <div class="carousel"> in my case, using element.children()... is there no easy way to reference it in angularjs? Looked around but no dice.

1

There are 1 best solutions below

1
On BEST ANSWER

You need to write one more directive that will be intimate angular code that ng-repeat rendered all the img tags then on that we will add listener event which will set the height and width of that element on load of that image.

Directive

.directive("carousel", ["$timeout", function ($timeout) {
    return {
        restrict: "E",
        transclude: true,
        template: "<div class=\"carousel\" ng-transclude></div>",
        link: function (scope, element, attrs) {
            var div = element.children();
            scope.$on('ngRepeatDone', function () {
                element.find('img').on('load', function () {
                    var images = div.children();
                    var minHeight = Math.min.apply(null, _.pluck(images, "height"));
                    angular.forEach([div, images], function (e) {
                        e.css({
                            height: minHeight + "px"
                        });
                    });
                    scope.$apply();
                });
            });
        }
    }
}])

NgRepeateDone

.directive('myPostRepeatDirective', function () {
    return function (scope, element, attrs) {
        if (scope.$last) {
            scope.$emit('ngRepeatDone')
        }
    };
});

Demo JSFiddle