Drag and drop with AngularJS and ng-repeat

I am using AngularJS and jQuery UI and have an app with drag-and-drop behaviour. I want to update a statistic on the screen every time an item is dropped.  The items are displayed in an  ng-repeat.

This turns out to be a bit tricky for a newcomer to AngularJS, e.g. you need to know:

I haven’t seen any examples that put all this together for my particular case, and though the solution is straightforward it took me quite a while to piece it together.

To save you the time, here is my solution.  To simplify it, it just updates a text string obj.outer. When you load up the page, the n and inner are replaced with the numbers 1,2,3 and “Inner linked”, as expected. outer is replaced with “Updated on link”, which is also what you would expect. You can drag and drop the pieces of text on each other. And when you do, the inner and outer fields are updated.

index.html:

    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.5/angular.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.1/underscore-min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/jquery-ui.min.js"></script>

    <div ng-app="test">
    <p ng-init="obj={outer:'not yet'}">outer: {{obj.outer}}</p>
      <ul>
          <li ng-repeat="n in [1,2,3]"><span my-dnd>{{n}} {{inner}}</span></li>
      </ul>
    </div>

app.js:

    angular.module('test', [])
      .directive('myDnd', function() {
        return {
            link: function(scope, elt, attrs) {
                scope.inner = "Inner";
                scope.obj.outer = "Updated on link";
                elt.draggable({revert:"invalid"});
                elt.droppable({
                    accept: function( d ) { return true; },
                    drop: function( event, ui ) {
                        return scope.$apply(function() {
                            scope.inner = 'Dropped';
                            scope.obj.outer = 'Updated on drop';
                        });
                    }
                });
              }
            }
        });

Some comments:

  • Note that you can use scope.obj.outer; you do not need to use scope.$parent.obj.outer. I believe this is ‘prototypical Javascript inheritance’. I’m not exactly sure why you need the $parent sometimes but not others.
  • You definitely need scope.obj.outer rather than just scope.outer.
  • You can leave out the return in the statement return scope.$apply(...). I haven’t looked into what the difference is. You cannot leave the call to $apply out.
  • I would like to know why the documentation shows the link function using scope rather than $scope, as used by the controllers.

Please let me know if you have any thoughts or improvements!

  

2 thoughts on “Drag and drop with AngularJS and ng-repeat”

  1. There is an easier way to do this. You can use ng-init to initialize the JQuery after Ng-repeat has done it’s job like this:

    and then put the JQuery $( “.draggable” ).draggable(); inside initDragAndDrop(). It works.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>