Saturday, November 24, 2012

Dealing with AngularJS + JQuery Masonry + ImagesLoaded plugins for a Pinterest like app


Recently I was working on a project where I wanted to create Pinterest like cards with different heights. Each 'card' can have images, comments, likes etc. I wanted to use Angularjs, Jquery Masonry plugin to achieve this.  But the problem was that in Angularjs doesn't provide easy way to deal with jquery plugins.

After doing lot of reading (AngularJS docs are GREAT btw), research &  googling, the closest I came to was: http://jsfiddle.net/roychoo/XVzUW/3/   (or my version: http://jsfiddle.net/rajaraodv/GwP5Z/1/)

Those things are great if the cards don't have images, but if you do have images, you get into issues. Because if you have images, you won't necessarily know their width and heights before hand until they are completely loaded.

Steps / Problem:

  1. ng-repeat will create dom elements
  2. masonry starts calculating width & hight of cards and starts to rearrange them
  3. BUT, images are not loaded so the height/width are wrong! so step2 will be wrong and you'll endup with stacked cards(one on top of the other)


Loading ....



If you set image's height & width to some static number (say 200px X 300px), then you'll make Masonry plugin happy (i.e. cards will be properly arranged), but you'll end up stretching/squeezing the images (especially if the images are very small).

Steps / problem:

  1. ng-repeat will create dom elements
  2. masonry starts calculating width & hight of cards and starts to rearrange them
  3. Masonry is happy BUT images look squeezed or stretched.



To minimize stretching/squeezing images, I had set max-height and max-width for images. But since these values are not constant, you will still get into masonry issue.

Steps / problem:

  1. ng-repeat will create dom elements
  2. masonry starts calculating width & hight of cards and starts to rearrange them
  3. BUT, AGAIN images are not loaded! so step2 will be wrong & you'll endup with stacked cards(one on top of the other)


Then I found I can easily deal with images using Imagesloaded (github) JQuery plugin. I thought, great and went to work.
Loading ....

To my surprise, that didn't work either!

Steps / problem:

So, I started digging into ImagesLoaded plugin's code and then realized that by the time ImagesLoaded starts to collect img's src, AngularJS hasn't yet added it. i.e. the ng-src="path/to/img" hasn't been copied to src="path/to/img" yet! And src was "" when ImagesLoaded plugin ran!


Finally.. Solution:

After some trial and error I wrapped the whole thing with a $timeout of 0 and that was enough for it to finally work!
Loading ....


PS: There may be a better way of solving this problem, if there is, please let me know.

3 comments:

  1. same problem here but how did you fix it, I mean where did you put the time out?

    ReplyDelete
  2. Replies
    1. You can use something like this:
      http://jsfiddle.net/NTsZx/
      Then use the following code together with the isotope/mansory constructor:

      $timeout(function(val) { //looks like timeout or deferred solves the issue
      scope.container.imagesLoaded(function() {

      Delete