Simple Drag and Drop in ExtJS

I recently needed to implement Drag and Drop in an ExtJS project. The requirement was to have a visible list of widgets on one panel. The user would then drag specific widgets to one of the recipient panels to create a clone of the original. I've created sample code to easily implement this behavior. It should work correctly for various components and containers.

Lets start with the code for the initial widgets. To make them draggable and assigned to a specific ddGroup, the following was added to the render event function of the component/container.


Drag Code

onCompRender: function (component, eOpts) {
  component.DragZone = Ext.create('Ext.dd.DragZone', component.getEl(), {
    // Override the getDragData function
    // Notice that we used the component element dom rather that the 
    // dom returned by the event. This is to get uniform behavior for 
    // containers
    getDragData: function (e){
        var compEl = component.getEl(), 
        cloneEl;

        // Clone the dom element
        cloneEl = compEl.dom.cloneNode(true);
        // generate an new Id
        cloneEl.id = Ext.id();

        // We must return ddel, repairXY is a nice feature. 
        // All other data is for the drop zone to consume
        return {
            ddel: cloneEl,
            repairXY: Ext.fly(e.getTarget()).getXY(),
            componentClone: component.cloneConfig()
        };      
    },
    getRepairXY: function(){
        return this.dragData.repairXY;
    },
    ddGroup: 'auGroup'
  });
}

Drag Code Explained

A new DragZone is instanciated and stored for possible later use in the component. Ext.dd.DragZone takes two parameters: the element that will host the dragzone (in our case: component.getEl()) and a config object.

The config object:

  1. Overrides the getDragData function
  2. Overrides the getRepairXY function
  3. Assigns a dd group

 The most important of these is the getDragData function. In short, this function:

  • Clones the DOM node that represents the widget we're dragging.
  • Assigns a new id to it so they're won't be a duplicate.
  • Returns a object contaiining:
    • ddel: our cloned DOM node
    • repairXY: the cordinates to snap back to for a failed drag.
    • componentClone: a clone of our original component/container to be added to the drop zone container.

Now we take a similar approach and add the code below to the render event function of the container that will receive the dropped widgets

 


Drop Code

onCompRender: function (component, eOpts) {
  component.dropZone = Ext.create("Ext.dd.DropZone",component.getEl(),{
    // Tell the zone what our target component is
    getTargetFromEvent: function(event) { 
        return component;
    },
    // When the node is dropped, add a new instance to the
    // the component via the supplied component clone
    onNodeDrop : function(target,dd,e,data){
        component.add(data.componentClone);
    },
    ddGroup: 'auGroup'
  });
}

Drop Code Explained

A new DropZone is instanciated and stored for possible later use in the component. Ext.dd.DropZone takes two parameters: the element that will host the dragzone (in our case: component.getEl()) and a config object.

The config object:

  • Overrides the getTargetFromEvent function
  • Overrides the onNodeDrop function
  • Assigns a dd group to the drop zone

The getTargetFromEvent function specifies the valid drop zone. In our case we simply return our container.

The onNodeDrop function is where the drop behavior is defined. Our previously discussed getDragData method returned an object. This object is passed as the fourth parameter of the function (named data in our example). The clone of the drag object is accessed via the aptly named variable: data.componentClone. This widget is simply added to our container which completes our successful drop.


Quick Example

Below are two screenshots of an example program. The first shows the state of the program initially. The small panel within the DragZone panel is our dragable widget. The beforeRender listener of this widget contains a single line of code:

component.setTitle(component.getId());

The widget is dragged over to the Valid Drop Zone panel and the clone of the original widget is added to the panel. Voila!