In this example you will use custom AngularJS directives to implement a set of draggable elements containing words that can be dragged onto a set of droppable image elements. When the word is dropped on an image, it is appended to a list of words that appear below the image.
The purpose of this exercise is to show you an example of using the HTML5 drag and drop events. The example only uses the events, and the actual drag and drop functionality is built using the AngularJS mechanisms. The reason for this is to illustrate using AngularJS (plus, frankly, the HTML5 drag and drop is not well implemented and needs to be revised). Another thing illustrated in this example is appending new elements to existing ones in an AngularJS directive.
The folder structure for this example is as follows:
■ ./server.js: Node.js web server that serves the static project files.
■ ./images: Folder that contains the images used in the examples.
■ ./ch11: Project folder.
■ ./ch11/dragdrop.html: AngularJS template for the project.
■ ./ch11/js/dragdrop.js: AngularJS application supporting the custom drag and drop directives.
The code in Listing 11.5 contains the dragdrop.js
application that defines two new custom AngularJS directives, dragit
and dropit
. Notice that in the parent scope the dragStatus
and dropStatus
variables are defined; these are updated in the custom directives. This is possible because no isolate scope is defined in the directives, so they share the parent controller scope.
Notice that inside the dragit
directive the HTML5 draggable attribute is added to the dragit
element using the attr()
method. Also in the dragit directive the dragstart
, drag
, and dragend
event handlers are implemented. For dragstart
and drag
, the default behavior is to allow the drag to start and dragenter
/dragleave
events to fire. However, dragend
does prevent the default behavior so that our custom AngularJS code can handle the drop.
Inside the dropit
directive, the dragover
, dragleave
, dragenter
, and drop
are implemented. Notice that in drop
we use the append method to append a <p>
element to the dropit
element. The value inside the paragraph comes from the scope and was set during dragstart
in the dragit
directive. Once again, this is possible because no isolate scopes are defined in the directives.
01 var app = angular.module('myApp', []);
02 app.controller('myController', function($scope) {
03 $scope.dragStatus = "none";
04 $scope.dropStatus = "none";
05 $scope.dropValue = "";
06 })
07 .directive('dragit', function($document, $window) {
08 function makeDraggable(scope, element, attr) {
09 angular.element(element).attr("draggable", "true");
10 element.on('dragstart', function(event) {
11 element.addClass('dragItem'),
12 scope.$apply(function(){
13 scope.dragStatus = "Dragging " + element.html();
14 scope.dropValue = element.html();
15 });
16 event.dataTransfer.setData('Text', element.html());
17 });
18 element.on('drag', function(event) {
19 scope.$apply(function(){
20 scope.dragStatus = "X: " + event.pageX +
21 " Y: " + event.pageY;
22 });
23 });
24 element.on('dragend', function(event) {
25 event.preventDefault();
26 element.removeClass('dragItem'),
27 });
28 }
29 return {
30 link: makeDraggable
31 };
32 })
33 .directive('dropit', function($document, $window) {
34 return {
35 restrict: 'E',
36 link: function makeDroppable(scope, element, attr){
37 element.on('dragover', function(event) {
38 event.preventDefault();
39 scope.$apply(function(){
40 scope.dropStatus = "Drag Over";
41 });
42 });
43 element.on('dragleave', function(event) {
44 event.preventDefault();
45 element.removeClass('dropItem'),
46 scope.$apply(function(){
47 scope.dropStatus = "Drag Leave";
48 });
49 });
50 element.on('dragenter', function(event) {
51 event.preventDefault();
52 element.addClass('dropItem'),
53 scope.$apply(function(){
54 scope.dropStatus = "Drag Enter";
55 });
56 });
57 element.on('drop', function(event) {
58 event.preventDefault();
59 element.removeClass('dropItem'),
60 scope.$apply(function(){
61 element.append('<p>' +
62 event.dataTransfer.getData('Text') + '</p>'),
63 scope.dropStatus = "Dropped " + scope.dropValue;
64 });
65 });
66 }
67 };
68 });
The code in Listing 11.6 implements the AngularJS template that displays the dragStatus
and dropStatus
values. Notice that the draggable elements are declared using the <dragit>
syntax and the droppable elements are declared using the <dropit>
syntax.
Figure 11.2 shows the working AngularJS application in action. As words are dragged and dropped onto the images, they are appended below the image. Also notice that the drag coordinates and drop status are displayed as well.
01 <!doctype html>
02 <html ng-app="myApp">
03 <head>
04 <title>HTML5 Draggable and Droppable Directives</title>
05 <style>
06 dropit, img, p{
07 vertical-align: top; text-align: center;
08 width: 100px;
09 display: inline-block;
10 }
11 p {
12 color: white; background-color: black;
13 font: bold 14px/16px arial;
14 margin: 0px; width: 96px;
15 border: 2px ridge grey;
16 background: linear-gradient(#888888, #000000);
17 }
18 span{
19 display:inline-block; width: 100px;
20 font: 16px/18px Georgia, serif; text-align: center;
21 padding: 2px;
22 background: linear-gradient(#FFFFFF, #888888);
23 }
24 .dragItem {
25 color: red;
26 opacity: .5;
27 }
28 .dropItem {
29 border: 3px solid red;
30 opacity: .5;
31 }
32 #dragItems {
33 width: 400px;
34 }
35 </style>
36 </head>
37 <body>
38 <h2>HTML5 Drag and Drop Components</h2>
39 <div ng-controller="myController">
40 Drag Status: {{dragStatus}}<br>
41 Drop Status: {{dropStatus}}
42 <hr>
43 <div id="dragItems">
44 <span dragit>Nature</span>
45 <span dragit>Landscape</span>
46 <span dragit>Flora</span>
47 <span dragit>Sunset</span>
48 <span dragit>Arch</span>
49 <span dragit>Beauty</span>
50 <span dragit>Inspiring</span>
51 <span dragit>Summer</span>
52 <span dragit>Fun</span>
53 </div>
54 <hr>
55 <dropit><img src="/images/arch.jpg" /></dropit>
56 <dropit><img src="/images/flower.jpg" /></dropit>
57 <dropit><img src="/images/cliff.jpg" /></dropit>
58 <dropit><img src="/images/jump.jpg" /></dropit>
59 </div>
60 <script src="http://code.angularjs.org/1.3.0/angular.min.js"></script>
61 <script src="js/dragdrop.js"></script>
62 </body>
63 </html>