3

I have an array or image URLs that I insert into a page with ng-repeat as <img> elements:

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

I would like to get dimensions (widths and heights) of those images from my controller after they've been inserted into DOM. Is there a way to iterate through my $scope.images array and get references to the DOM nodes that represent its elements? If not, what would be the best way to do that?

n1313
  • 20,555
  • 7
  • 31
  • 46
  • 1
    i had similar question and solved it with a directive: http://stackoverflow.com/questions/17547917/angularjs-image-onload-event – Ivan Chernykh Oct 10 '13 at 10:07
  • Thanks for the suggestion. It's a bit clunky, I guess, but should work. I am lucky to be working with images, as they have an onload event. Maybe there is a more generic approach? – n1313 Oct 10 '13 at 10:18
  • I've asked this question 3 months ago , it has 1000 views but only one answer with several upvotes.. seems like it is "not bad practice" after all.. ;) – Ivan Chernykh Oct 10 '13 at 10:22
  • As I look closer to that suggestion, it doesn't really work for me. Yes, I am able to modify the node from the directive, but I'm still unable to get to that node from the controller. Maybe I should `$emit` an event with image ID and node and add node parameters to the corresponding array element in my controller? Even clunkier... :( – n1313 Oct 10 '13 at 10:28
  • 2
    have a look: http://jsfiddle.net/XqeCr/ , you can actually get a relevant item/object from the scope and update it directly in a directive! – Ivan Chernykh Oct 10 '13 at 10:44
  • Okay, that is cool, I like that. Thanks! Could you please make an answer out of that so I could accept it? – n1313 Oct 10 '13 at 10:49
  • great! i like to solve this kind of issues in Angular! – Ivan Chernykh Oct 10 '13 at 10:54

2 Answers2

2

You can get a specific object and directly change it in a directive, so if you're using: ng-repeat="img in imgs" , you'll have scope.img in a directive:

.directive('loadable', function () {       
    return {
        link: function(scope, element, attrs) {   

            element.bind("load" , function(e){ 

               // this is it:

                scope.img.dimensions = {
                    height: element[0].naturalHeight,
                    width: element[0].naturalWidth
                }

                console.log(scope.img) // now the original object (in scope)
                                       // has dimensions property!

            });

        }
    }
});

Example : http://jsfiddle.net/XqeCr/1/

Update And it is recommended to wrap object's manipulations in scope's $aplly method call:

     scope.$apply( function(){
         scope.img.dimensions = {
             height: element[0].naturalHeight,
             width: element[0].naturalWidth
         }
     });

Updated example: http://jsfiddle.net/XqeCr/4/

Ivan Chernykh
  • 41,617
  • 13
  • 134
  • 146
1

You should not access DOM from controller - then you won't be able to test your controller, for example. Instead write a directive that will put onload handler on those images and once it is called - it will put put width&height into scope

function(scope, el){
  el.find('img').on('load', function(){
    scope.img.width = this.width;
    scope.img.height = this.width;
  })
}

and in your controller $watch for this changes and do smth once data has been populated. Pay attention how you set data into scope from directive - you should have dot in expression to not get data locked in child scope - see this article: http://jimhoskins.com/2012/12/14/nested-scopes-in-angularjs.html

4vanger
  • 125
  • 1
  • 3