AngularJS directives with HTML5 drag and drop -- issue with scope object

13,681

There are a couple of typos in the fiddle, but the basic problem is that your drag events are outside an angular digest cycle. You should wrap your changes in $scope.$apply (code sample coming). This forked and bugfixed (FIDDLE) shows that when you click the button, angular shows the changes and refreshes the display with new values.

Fix: (FIDDLE)

$scope.$apply(function() {
    $scope.items.push(dataText);
});

A bug you had is in this code:

<span ng-repeat="items in items">{{item.name}},</span>

This should probably be ng-repeat="item in items", also items only contains the dropped text so it is an array of strings and not the original item objects.

Share:
13,681
Skylude
Author by

Skylude

Updated on June 19, 2022

Comments

  • Skylude
    Skylude about 2 years

    I'm fairly new to angular and I'm having a hard time wrapping my head around where the items are being pushed to. I am not sure if I am correctly setting up the functions to be used with drag/drop and if its getting bound to an older scope object and the ng-repeat isn't being updated properly. I'm thinking there is some slight issue with the way I have this setup. Any pointers or help would be much appreciated.

    What should happen is when you drag a color from the Draggable container into the Droppable container it should update the text which is linked to the scope object items. I am successfully pushing an item onto the scope object but ng-repeat isn't picking it up. I am not sure if I need a watch or what to do to get it to pay attention to the newly added items.

    JS Fiddle Here: http://jsfiddle.net/RV23R/

    HTML CODE:

    <div ng-app="my-app" ng-controller="MainController">
    <div class="container">
        <header><h1>Draggables</h1></header>
        <section>
            <div draggable="true" ng-repeat="drag_type in drag_types">{{drag_type.name}}</div>
        </section>
    </div>
    <div class="container">
        <header><h1>Drop Schtuff Here</h1></header>
        <section droppable="true">
            <div><span>You dragged in: </span><span ng-repeat="items in items">{{item.name}},</span></div>
        </section>
    </div>
    

    ANGULAR CODE:

    var module = angular.module('my-app', []);
    
    module.directive('draggable', function () {
      return {
        restrict: 'A',
        link: function (scope, element, attrs) {
          element[0].addEventListener('dragstart', scope.handleDragStart, false);
          element[0].addEventListener('dragend', scope.handleDragEnd, false);
        }
      }
    });
    
    module.directive('droppable', function () {
      return {
        restrict: 'A',
        link: function (scope, element, attrs) {
          element[0].addEventListener('drop', scope.handleDrop, false);
          element[0].addEventListener('dragover', scope.handleDragOver, false);
        }
      }
    });
    
    function MainController($scope)
    {
        $scope.drag_types = [
            {name: "Blue"},
            {name: "Red"},
            {name: "Green"},
        ];
        $scope.items = [];
    
        $scope.handleDragStart = function(e){
            this.style.opacity = '0.4';
            e.dataTransfer.setData('text/plain', this.innerHTML);
        };
    
        $scope.handleDragEnd = function(e){
            this.style.opacity = '1.0';
        };
    
        $scope.handleDrop = function(e){
            e.preventDefault();
            e.stopPropagation();
            var dataText = e.dataTransfer.getData('text/plain');
            $scope.items.push(dataText);
            console.log($scope.items);
        };
    
        $scope.handleDragOver = function (e) {
            e.preventDefault(); // Necessary. Allows us to drop.
            e.dataTransfer.dropEffect = 'move';  // See the section on the DataTransfer object.
            return false;
      };
    
    }
    

    CSS (if anyone cares)

    .container {
      width: 600px;
      border: 1px solid #CCC;
      box-shadow: 0 1px 5px #CCC;
      border-radius: 5px;
      font-family: verdana;
      margin: 25px auto;
    }
    
    .container header {
      background: #f1f1f1;
      background-image: -webkit-linear-gradient( top, #f1f1f1, #CCC );
      background-image: -ms-linear-gradient( top, #f1f1f1, #CCC );
      background-image: -moz-linear-gradient( top, #f1f1f1, #CCC );
      background-image: -o-linear-gradient( top, #f1f1f1, #CCC );
      box-shadow: 0 1px 2px #888;
      padding: 10px;
    }
    
    .container h1 {
      padding: 0;
      margin: 0;
      font-size: 16px;
      font-weight: normal;
      text-shadow: 0 1px 2px white;
      color: #888;
      text-align: center;
    }
    
    .container section {
      padding: 10px 30px; 
      font-size: 12px;
      line-height: 175%;
      color: #333;
    }