Packery not working on AngularJS if i push additional widgets in my array

342 views Asked by At

I searched for an considerable amount of time now but i still can't find a solution to my problem. I don't know anything about packery but i found this code which works perfectly and does what i wanted to do.

directive('workspace', ['$rootScope', function($rootScope) {
 return {
constrain: 'A',
link: function(scope, element, attrs) {
  element.ready(function() {
    var packery = new Packery(element[0], {
      rowHeight: '.module-sizer',
      itemSelector: '.module',
      columnWidth: '.module-sizer'
    });
    angular.forEach(packery.getItemElements(), function(item) {
      var draggable = new Draggabilly(item);
      packery.bindDraggabillyEvents(draggable);
    });
    packery.layout();
  });
} }; }]).

So it was working perfectly when i had an array of widgets where i was not adding or removing an element but simply using ng-show to hide/show them. Now I no longer want to use ng-show, instead i am adding and deleting my widgets from an initial empty array

.controller('WidgetCtrl', ['$scope', function ($scope) {
$scope.counter = 0;
$scope.current = 0;
$scope.widgets = [];


$scope.addWidget = function(name){
  var widgets = {
    widget1: {name: 'widget1', id: ''},
    widget2: {name: 'widget2', data: {dataVariable: 'some data'}, id:''}
  };
  var widget = widgets[name];

 if (widget) {
    $scope.widgets.push(widget);
    $scope.widgets[$scope.current].id = $scope.widgets.length-1;
    console.log('index of the last widget added: ' + $scope.widgets[$scope.current].id);
    $scope.current++;}

So the only widgets that can be dragged are those who are initially in the array. Any widgets i add after the page has loaded will not work. I am new in angular and i was reading on $scope.apply and recompile a directive but i am not sure if its related to my problem

<div class="module-container" workspace>                                
                            <div class="module-sizer"></div>
                            <div class="gutter-sizer"></div>
            <div class="module" ng-repeat='widget in widgets'>
                <div dynamic-widget='widget.name' data='widget.data'> </div>
            </div>
      </div>
1

There are 1 answers

0
gdgr On BEST ANSWER

The issue here is to do with the fact packery binds events to grid items in order to perform layout and to make them 'draggable', here's the snippet responsible for that:

angular.forEach(packery.getItemElements(), function(item) {
      var draggable = new Draggabilly(item);
      packery.bindDraggabillyEvents(draggable);
});

So what's happening presently with the way your directive is set up is that when the template containing the directive initially loads, it's picking up any matching items .module and initialising packery & draggabilly with the above code and pckry.layout();, from that point onwards there's no way of your app knowing that the number of widgets has increased at all.

What you can do to make sure they're initialised when added is to pass the scope into the directive, as follows:

constrain: 'A',
scope: true,
link: // ...

And then you'll want to add a $scope.$watchCollection to the array widgets like so:

$scope.$watchCollection('widgets', function() {
            // Call packery and draggabilly on elems here
});

I guess you'll want to move the logic for initialising packery into a separate function for better code separation, though be aware that the $watchCollection will fire the first time the directive runs anyway so all the logic could just be inside there.

One final thing to note is that you may need to call pckry.destroy(); beforehand to ensure the desired result. Unfortunately I don't have enough time to make an example so this really is just a thought experiment, but I hope it helps!