Search code examples
extjsdrag-and-dropextjs6-modern

ExtJS Drag and Drop in tree on modern toolkit


Actually I'm going to implement a tree view, where the user should have the option to reorder the structure with drag and drop. Actually I can't figure out how to enable drag and drop. I found a lot of examples using the 'treeviewdragdrop' plugin, which is just working with the classic toolkit.

The following Code made me move the first node but not more.

this.toParentSource = new Ext.drag.Source({
                element: this.getView().element.down('.x-gridcell'),
                constrain: {
                    element: this.getView().body,
                    vertical: true
                }
            });

Can you help me with this problem? I'm using ExtJS 6.5.2 modern toolkit.


Solution

  • This is how I enabled drag and drop for trees in modern Ext JS:

    First I've written a plugin which creates the sources that should be draggable.

    Plugin

      Ext.define('test.component.plugin.TreeDragger', {
    extend: 'Ext.AbstractPlugin',
    alias: 'plugin.treedrag',
    
    mixins: ['Ext.mixin.Observable'],
    
    constructor: function (config) {
        this.mixins.observable.constructor.call(this, config);
    },
    
    init: function (component) {
        var me = this;
    
        this.source = new Ext.drag.Source({
            element: component.element,
            handle: '.x-gridrow',
            constrain: {
                element: true,
                vertical: true
            },
            describe: function (info) {
                var row = Ext.Component.from(info.eventTarget, component);
                info.row = row;
                info.record = row.getRecord();
            },
            proxy: {
                type: 'placeholder',
                getElement: function (info) {
                    console.log('proxy: getElement');                    
    
                    var el = Ext.getBody().createChild({
                        style: 'padding: 10px; width: 100px; border: 1px solid gray; color: red;',
                    });
                    el.show().update(info.record.get('description'));
                    return el;
                }
            },
            // autoDestroy: false,
            listeners: {
                scope: me,
                beforedragstart: me.makeRelayer('beforedragstart'),
                dragstart: me.makeRelayer('dragstart'),
                dragmove: me.makeRelayer('dragmove'),
                dragend: me.makeRelayer('dragend')
            }
        });
    },
    
    disable: function () {
        this.source.disable();
    },
    
    enable: function () {
        this.source.enable();
    },
    
    doDestroy: function () {
        Ext.destroy(this.source);
        this.callParent();
    },
    
    makeRelayer: function (name) {
        var me = this;
        return function (source, info) {
            return me.fireEvent(name, me, info);
        };
    }
    });
    

    Next I used this plugin inside my tree.

    Tree

    xtype: 'tree',
        hideHeaders: true,
    
        plugins: {
            treedrag: {
                type: 'treedrag',
                listeners: {
                    beforedragstart: function (plugin, info) {
                        // logic to identify the root and prevent it from being moved
                        console.log('listeners: beforedragstart');
                    }
                }
            }
        },
        columns: [{
                xtype: 'treecolumn',
                flex: 1,
            }
        ]
    

    Then I defined the drop targets inside the controller.

    Controller

    afterLoadApportionmentObjectsForTree: function (succes) {
        if (succes) {
    
            tree = this.getView().down('tree');
            if (tree) {
                tree.expandAll();
                tree.updateHideHeaders(tree.getHideHeaders());
                var store = tree.getStore();
                store.remoteFilter = false;
                store.filterer = 'bottomup';
    
                this.createDropTargets();
            }
        }
    },
    
    createDropTargets: function () {
        var me = this,
            rows = tree.innerItems;
        Ext.each(rows, function (el) {
            var target = new Ext.drag.Target({
                element: el.element,
                listeners: {
                    scope: me,
                    drop: me.onDrop,
                    beforeDrop: me.onBeforeDrop
                }
            });
        });
    },
    
    onDrop: function (target, info, eOpts) {
        var source = info.record,
            row = Ext.Component.from(target.getElement(), tree),
            destination = row.getRecord(),
            parentNode = source.parentNode;
    
        destination.appendChild(source);
        destination.expand();
    
        if (!parentNode.hasChildNodes()) {
            parentNode.set('leaf', true);
        }
    },
    
    onBeforeDrop: function (target, info, eOpts) {
        var source = info.record,
            row = Ext.Component.from(target.getElement(), tree),
            destination = row.getRecord();
        // prevent the user to drop the node on itself
        // this would lead to an error caused by recursive method calls 
        if (source == destination) {
            return false;
        }
        // prevent the user to drop a node on it's children
        // this would lead to an error caused by recursive method calls
        if (source.findChild('number', destination.get('number'), true) != null) {
            return false;
        }
    
        return true;
    }