AngularJS зидария за динамично променящи се височини

Имам div, които се разширяват и свиват, когато се щракне върху тях. Библиотеката на Masonry работи чудесно за инициализиране на страницата. Проблемът, който изпитвам, е, че с абсолютното позициониране на място от Masonry и директивата по-долу, когато div се разширяват, те се припокриват с div по-долу. Трябва да накарам divs под разширяващия се div да се преместят надолу, за да се справят с разширяването.

Моите източници са: http://masonry.desandro.com/

и

https://github.com/passy/angular-masonry/blob/master/src/angular-masonry.js

/*!
* angular-masonry <%= pkg.version %>
* 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 > 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 <masonry loaded="callback($element)">
      // 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 && 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 && index !== scope.$index) {
            ctrl.scheduleMasonryOnce('reloadItems');
            ctrl.scheduleMasonryOnce('layout');
          }
          index = scope.$index;
        });
      }
    }
  };
});
}());

person Yahiko Kikikoto    schedule 14.05.2014    source източник
comment
Решението, което вярвам, е да стартирам masonry отново след едно от тези събития, но не успях да разбера как да направя това от контролера Angular, в който го използвам. Добавих премия към този въпрос .   -  person Guy    schedule 31.05.2014
comment
Моля, направете обикновен плункер, демонстриращ проблема   -  person Gil Birman    schedule 01.06.2014
comment
Или можете просто да напишете по-проста директива: plnkr.co/edit/UyRS0clrCwDpSrYgBsXS?p=preview   -  person SoluableNonagon    schedule 13.06.2014


Отговори (2)


Подобно на много не-Angular библиотеки, изглежда, че отговорът се крие в опаковането на библиотеката в Angular директива.

Не съм го пробвал, но изглежда, че това е, което този човек направи

person Garrett McCullough    schedule 03.06.2014
comment
Да, OP включи тази връзка като една от 2-те библиотеки, които той използва. Проблемът, който се опитваме да разрешим, е как да извикате отново функцията за зидария от вашия контролер, за да й кажете да обнови, защото сте направили промени в размера на компонентите, които са на страницата. - person Guy; 03.06.2014

Можете да използвате функциите $emit, $broadcast и $on на Angular.

Вътре във вашата функция за връзка с директива masonry:

scope.$on('$resizeMasonry', ctrl.scheduleMasonryOnce('layout'));

Вътре във вашата функция за връзка с директива masonryBrick или всеки друг дъщерен елемент:

scope.$emit('$resizeMasonry');

Използвайте $emit, за да изпратите събитие нагоре в дървото на обхвата и $broadcast, за да изпратите събитие надолу в дървото на обхвата.

person Travis    schedule 06.06.2014
comment
Благодаря Травис. Представете си, че сте включили както зидария, така и ъглова зидария в проект и във вашия контролер на страница сте добавили елемент чрез да речем събитие с кликване. Този елемент сега припокрива друг елемент. Как сега бихте извикали опресняване/презареждане на зидарията от контролера на страницата? - person Guy; 06.06.2014
comment
@Guy, това ще зависи къде е обхватът на контролера по отношение на MasonryCtrl. Ако това е дъщерен обхват, ще искате да $emit към родителски обхват. Ако това е родителски обхват, използвайте $broadcast. В краен случай $rootScope.$broadcast ще се процеди до всички обхвати. - person Travis; 07.06.2014