Angular Directives watching external models

angularjs, angularjs-directive, javascript

Solution

I think Beyers is right that watches are actually pretty efficient.

Still, here's how you could do this with a single watch by using a parent directive for the whole image gallery:

myApp.directive('imageGallery', function() {
return {
    restrict: 'A',
    controller: function($scope, $element, $attrs) {
        function updatePhotos() {
            $element.children().attr('style',  'transition: .3s; width: ' + $scope.thumbWidth + 'px; height: ' + $scope.thumbWidth + 'px;');
            $scope.photos.forEach(function(photo){
                photo.url = $scope.zoomSize < 5 ? photo.thumb_url : photo.medium_url;
            });
        };

        this.updatePhotos = updatePhotos;
    },
    link: function(scope, element, attrs, ctrl) {
        scope.$watch('thumbWidth', function() {   
            ctrl.updatePhotos();
        });
        ctrl.updatePhotos();
    }
}
});

(full fiddle here)

Notice that I've put the central updating logic into the `controller` rather than the `link` function. That's not strictly necessary here, but it could be useful in the future, because that controller object would be visible to the child `galPhoto` directives. (See the two "NOTE" comments in the fiddle.)

Problem

In learning Angular I am creating a simple gallery that can be zoomed. My initial implementation used a simple ng-repeat, but I quickly discovered that based on the zoom of the gallery I would want to change things like the url source (from small to medium thumbs), and probably the css on captions etc. ``` <div class="photoHolder" ng-repeat="photo in photos | filter:{caption:query} | orderBy:orderProp" ng-attr-style="transition: .3s ease; width: {{thumbWidth}}px; height: {{thumbWidth}}px;"> <div class="photoClass" data-id="{{photo.id}}" ng-attr-style="background-image:url({{zoomSize < 5 ? photo.thumb_url : photo.medium_url}})"></div> <div class="caption">{{photo.caption}}</div> <div class="caption">{{photo.date}}</div> </div> ``` So, I switched to a much cleaner directive: ``` <gal-photo class="photoHolder" ng-repeat="photo in photos | filter:{caption:query} | orderBy:orderProp"/> ``` but the only way I could get the directive element to respond to the zoom change is to add a watch to the zoom inside the link for the element: ``` link: function(scope, element, attrs) { var image = new Image(); scope.photo.url = scope.zoomSize < 5 ? scope.photo.thumb_url : scope.photo.medium_url; scope.$watch('thumbWidth', function() { scope.photo.url = scope.zoomSize < 5 ? scope.photo.thumb_url : scope.photo.medium_url; element.attr('style', 'transition: .3s; width: ' + scope.thumbWidth + 'px; height: ' + scope.thumbWidth + 'px;'); }); } ``` I know that you aren't supposed to abuse watches, and my fear is that with 500 elements in the gallery you are doing 500 watches. But I can find no guidance on responding to external inputs within repeating directives. The two galleries seem to perform about the same, but I am wondering if I am doing this wrong? Here is a fiddle for the original gallery and a fiddle for the directive version.

Original source

Related problems