Drag and Drop basics with Ext JS

   Front End Development

ExtJS drag and drop basics

There are a lot of places where a developer can use a “Drag and Drop” implementation to help the user complete a certain process in the application. Not all applications have a need for this feature. Sometimes it is not the best way to solve an interaction with your application and as it can result in a decrease of usability. However, “Drag and Drop” is a natural way to interact with your application for some workflows. Placed in a proper UX setting, it can become a very powerful and elegant way to allow users to accomplish tasks.

ExtJS offers a very clear and easy implementation of DnD for your web apps, in this post we are going to learn how “Drag and Drop” works and how we can use it in our applications.

Four steps to “Drag and Drop”.

The foundation of drag and drop operations can be boiled down to four simple operations.

  1. Move the mouse or pointing device to the object.
  2. Select the object.
  3. “Drag” the object to the target location.
  4. “Drop” the object in the desired location.

The classes involved in this operation are located in the Ext.dd namespace. The following image lists all the classes involved in the “Drag and Drop” implementation.

ddpackage

There are various “Drag and Drop” implementations around the framework, like ordering elements of a GridPanel, GridPanel to GridPanel, GridPanel to FormPanel and more. Using the classes above you can make any visual component of the framework draggable and droppable. To understand the basic concepts of “Drag and Drop” we are going to implement dragging dropping items between two DIV elements.

Drag and drop basic implementation.

Imagine that we have a restaurant application and we need to arrange the tables in the main room, first we need two containers or divs, one for the tables and another one for the main room. You can find the complete working example here .

restaurant

Here’s the CSS & HTML required for this example.

<style>
#content{
    width:80%;
    height:400px;
    padding:10px;
    border:1px solid #000;
}
#tables{
    float:left;
    width:40%;
    height:100%;
    border:1px solid #AAA;
     background-color:rgba(222, 222, 222, 1.0);
}
#mainRoom{
    float:left;
    width:55%;
    height:100%;
    margin-left:15px;
    border:1px solid #AAA;
     background-color:rgba(222, 222, 222, 1.0);
}
.table{
    background-color:rgba(254, 108, 98, 1.0);
    border-radius:2px;
    border:1px solid gray;    
    width:64px;
    height:64px;
    margin:10px;
    color:#FFF;
    cursor:pointer;
    text-align:right;
    display: inline-block;
}
.valid-zone{
    background-color:rgba(157, 229, 86, 1.0) !important;
}
.selected{
    opacity:0.5;
}
</style>

<div id="content">   
    <div id="tables"> 
        <div class="table">1</div>
        <div class="table">2</div>
        <div class="table">3</div>
        <div class="table">4</div>

        <div class="table">5</div>
        <div class="table">6</div>
        <div class="table">7</div>
        <div class="table">8</div>

        <div class="table">9</div>
        <div class="table">10</div>
        <div class="table">11</div>
        <div class="table">12</div>
    </div>
     <div id="mainRoom"></div>
</div>

First we need to make our tables draggable.

 Ext.application({
    name: 'DnD',
    launch: function() {
        var tables = Ext.get('tables').select('div');
        Ext.each(tables.elements, function(el) {
            var dd = Ext.create('Ext.dd.DD', el, 'tablesDDGroup', {
                isTarget: false
            });
        });
    }
 });

<a title="DnD step one" href="https://fiddle.sencha.com/fiddle/6al" target="_blank">Code here</a>

In the code above we get each table div and make them draggable and assign them to a “Drag and Drop” group. Once we can move our tables around we need to set the drop target.

 //we set the main room div as a drop target
 var mainTarget = Ext.create('Ext.dd.DDTarget', 'mainRoom', 'tablesDDGroup', {
     ignoreSelf: false
 });

<a title="DnD step 2" href="https://fiddle.sencha.com/fiddle/6am" target="_blank">Code here</a>

Now we have to override the Drag and Drop methods

 var overrides = {
     startDrag: function(e) {
         console.log('startDrag');
     },
     onDrag: function() {
         console.log('onDrag');
     },
     onDragEnter: function(e, id) {
         console.log('onDragEnter');
     },
     onDragOver: function(e, id) {
         console.log('onDragOver');
     },
     onDragOut: function(e, id) {
         console.log('onDragOut');
     },
     onDragDrop: function(e, id) {
         console.log('onDragDrop');
     },
     onInvalidDrop: function() {
         console.log('onInvalidDrop');
     },
     endDrag: function(e, id) {
       console.log('endDrag');
     }
 };

<a title="DnD step three" href="https://fiddle.sencha.com/fiddle/6an" target="_blank">Code here</a>

And we apply it to our dd object

Ext.each(tables.elements, function(el) {
     var dd = Ext.create('Ext.dd.DD', el, 'tablesDDGroup', {
         isTarget: false
     });
     Ext.apply(dd, overrides);
 });

<a title="DnD step four" href="https://fiddle.sencha.com/fiddle/6ao" target="_blank">Code here</a>

No we have something like this.

1_drag

It is time to implement each method we have overridden, the first methods we need to override are startDrag and onDrag so we can add some logic when the item starts to drag and it follows our mouse pointer.

 startDrag: function(e) {
     //shortcut to access our element later
     if (!this.el) {
         this.el = Ext.get(this.getEl());
     }
     //add a css class to add some transparency to our div
     this.el.addCls('selected');
     //when we drop our item on an invalid place we need to return it to its initial position
     this.initialPosition = this.el.getXY();
  },
  onDrag: function(e) {
      //move the item where the mouse moves
      this.el.moveTo(e.getPageX() - 32, e.getPageY() - 32);
  },

<a title="DnD step five" href="https://fiddle.sencha.com/fiddle/6ap" target="_blank">Code here</a>

If we drop the item on a not valid drop zone we need to move it to its initial position.

onInvalidDrop: function() {
     this.el.removeCls('drop-target');
     this.el.moveTo(this.initialPosition[0], this.initialPosition[1]);
 },

<a title="DnD step six" href="https://fiddle.sencha.com/fiddle/6aq" target="_blank">Code here</a>

When the item is dragged over a valid drop position we are going to change the color of that zone overriding the next method.

onDragEnter: function(e, id) {
     Ext.fly(id).addCls(‘valid-zone');
 },
<a title="DnD step seven" href="https://fiddle.sencha.com/fiddle/6as" target="_blank">Code here</a>

So now when the user is not in a valid drop position, lets remove the color

 onDragOut: function(e, id) {
     Ext.fly(id).removeCls('valid-zone');
 },
<a title="DnD step eight" href="https://fiddle.sencha.com/fiddle/6at" target="_blank">Code here</a>

And finally we do the logic for dropping our item in a valid zone.

 onDragDrop: function(e, id) {
     // change the item position to absolute
     this.el.dom.style.position ='absolute';
     //move the item to the mouse position
     this.el.moveTo(e.getPageX() - 32, e.getPageY() - 32);
     Ext.fly(id).removeCls('valid-zone');
 },
 endDrag: function(e, id) {
     this.el.removeCls('selected');
     this.el.highlight();
 }

<a title="DnD step nine" href="https://fiddle.sencha.com/fiddle/6au" target="_blank">Code here</a>

This is how the “Drag and Drop” implementation should look.

1_drag

“Drag and Drop” using ExtJS is really easy and straightforward. The Ext.dd.DD class provides all the methods to help us monitor each phase of the “Drag and Drop” process. Implementing this patter in GridPanels, TreePanels, Forms is really straight forward too, you can se more info about this in de docs, in the Drag and Drop examples section.


Like What You See?

Got any questions?