title: AngularJS Masonry for Dynamically changing heights
body: <p>I have divs that expand and contract when clicked on.  The Masonry library has worked great for initializing the page.  The problem I am experiencing is that with the absolute positioning in place from Masonry and the directive below, when divs expand they overlap with the divs below.  I need to have the divs below the expanding div move down to deal with the expansion.</p>
  
  <p>My sources are:
  <a href="http://masonry.desandro.com/" rel="noreferrer">http://masonry.desandro.com/</a></p>
  
  <p>and</p>
  
  <p><a href="https://github.com/passy/angular-masonry/blob/master/src/angular-masonry.js" rel="noreferrer">https://github.com/passy/angular-masonry/blob/master/src/angular-masonry.js</a></p>
  
  <pre><code>/*!
  * angular-masonry &lt;%= pkg.version %&gt;
  * Pascal Hartig, weluse GmbH, http://weluse.de/
  * License: MIT
  */
  (function () {
    'use strict';
  
  angular.module('wu.masonry', [])
  .controller('MasonryCtrl', function controller($scope, $element, $timeout) {
    var bricks = {};
    var schedule = [];
    var destroyed = false;
    var self = this;
    var timeout = null;
  
    this.preserveOrder = false;
    this.loadImages = true;
  
    this.scheduleMasonryOnce = function scheduleMasonryOnce() {
      var args = arguments;
      var found = schedule.filter(function filterFn(item) {
        return item[0] === args[0];
      }).length &gt; 0;
  
      if (!found) {
        this.scheduleMasonry.apply(null, arguments);
      }
    };
  
    // Make sure it's only executed once within a reasonable time-frame in
    // case multiple elements are removed or added at once.
    this.scheduleMasonry = function scheduleMasonry() {
      if (timeout) {
        $timeout.cancel(timeout);
      }
  
      schedule.push([].slice.call(arguments));
  
      timeout = $timeout(function runMasonry() {
        if (destroyed) {
          return;
        }
        schedule.forEach(function scheduleForEach(args) {
          $element.masonry.apply($element, args);
        });
        schedule = [];
      }, 30);
    };
  
    function defaultLoaded($element) {
      $element.addClass('loaded');
    }
  
    this.appendBrick = function appendBrick(element, id) {
      if (destroyed) {
        return;
      }
  
      function _append() {
        if (Object.keys(bricks).length === 0) {
          $element.masonry('resize');
        }
        if (bricks[id] === undefined) {
          // Keep track of added elements.
          bricks[id] = true;
          defaultLoaded(element);
          $element.masonry('appended', element, true);
        }
      }
  
      function _layout() {
        // I wanted to make this dynamic but ran into huuuge memory leaks
        // that I couldn't fix. If you know how to dynamically add a
        // callback so one could say &lt;masonry loaded="callback($element)"&gt;
        // please submit a pull request!
        self.scheduleMasonryOnce('layout');
      }
  
      if (!self.loadImages){
        _append();
        _layout();
      } else if (self.preserveOrder) {
        _append();
        element.imagesLoaded(_layout);
      } else {
        element.imagesLoaded(function imagesLoaded() {
          _append();
          _layout();
        });
      }
    };
  
    this.removeBrick = function removeBrick(id, element) {
      if (destroyed) {
        return;
      }
  
      delete bricks[id];
      $element.masonry('remove', element);
      this.scheduleMasonryOnce('layout');
    };
  
    this.destroy = function destroy() {
      destroyed = true;
  
      if ($element.data('masonry')) {
        // Gently uninitialize if still present
        $element.masonry('destroy');
      }
      $scope.$emit('masonry.destroyed');
  
      bricks = [];
    };
  
    this.reload = function reload() {
      $element.masonry();
      $scope.$emit('masonry.reloaded');
    };
  
  
  }).directive('masonry', function masonryDirective() {
    return {
      restrict: 'AE',
      controller: 'MasonryCtrl',
      link: {
        pre: function preLink(scope, element, attrs, ctrl) {
          var attrOptions = scope.$eval(attrs.masonry || attrs.masonryOptions);
          var options = angular.extend({
            itemSelector: attrs.itemSelector || '.masonry-brick',
            columnWidth: parseInt(attrs.columnWidth, 10) || attrs.columnWidth
          }, attrOptions || {});
          element.masonry(options);
          var loadImages = scope.$eval(attrs.loadImages);
          ctrl.loadImages = loadImages !== false;
          var preserveOrder = scope.$eval(attrs.preserveOrder);
          ctrl.preserveOrder = (preserveOrder !== false &amp;&amp; attrs.preserveOrder !== undefined);
  
          scope.$emit('masonry.created', element);
          scope.$on('$destroy', ctrl.destroy);
        }
      }
    };
  }).directive('masonryBrick', function masonryBrickDirective() {
    return {
      restrict: 'AC',
      require: '^masonry',
      scope: true,
      link: {
        pre: function preLink(scope, element, attrs, ctrl) {
          var id = scope.$id, index;
  
          ctrl.appendBrick(element, id);
          element.on('$destroy', function () {
            ctrl.removeBrick(id, element);
          });
  
          scope.$on('masonry.reload', function () {
            ctrl.scheduleMasonryOnce('reloadItems');
            ctrl.scheduleMasonryOnce('layout');
          });
  
          scope.$watch('$index', function () {
            if (index !== undefined &amp;&amp; index !== scope.$index) {
              ctrl.scheduleMasonryOnce('reloadItems');
              ctrl.scheduleMasonryOnce('layout');
            }
            index = scope.$index;
          });
        }
      }
    };
  });
  }());
  </code></pre>