suamikim suamikim - 1 month ago 16
Javascript Question

Grid Drag & Drop: Suppress for some records (groups)

Hy there,

is it possible to control which records can be dragged and where they can be dropped (suppress drag-operation either right from the beginning or in the middle during hovering)?

What i need in detail is the following:

I'm having a grid with some groups (lets say male & female) and only want to activate the d&d inside group 'female' which means 2 things:

1.) I started dragging a record from group 'female' (Lisa). As soon as the drag is outside the group 'female' (above group 'male'...) it should display an error-state like when dragging outside the bounds of the grid:

Example of the error-state

2.) Starting to drag an item from group 'male' should either not be possible at all (just don't show the d&d panel) or show the error-state like mentioned above right from the beginning and never change to "correct"-state.

Thanks,

mike

Answer

After some digging around in the sources of ext i just found a solution which works but isn't perfect at all:

The "drop-allowed-indication" can be handled by the underlying DropZone which is created in onViewRender of the treeviewdragdrop-plugin. This is not documented but can be seen in the source-code of the plugin.

Everything that needs to be done (at least for this example) is to override/extend the onNodeOver- & onContainerOver-method of the DropZone to return the appropriate css-class for the drop-not-allowed- or drop-allowed-indication.

Ext.override(Ext.view.DropZone, {
    onNodeOver: function(nodeData, source, e, data) {
        if (data && data.records && data.records[0]) {
            // The check should be better specified, e.g. a
            // female with the name 'Malena' would be recognized as male!
            if (nodeData.innerHTML.indexOf(data.records[0].get('sex')) < 0) {
                return this.dropNotAllowed;
            }
        }
        return this.callOverridden([nodeData, source, e, data]);
    },
    onContainerOver: function(source, e, data) {
        return this.dropNotAllowed;
    }
});

Working example: http://jsfiddle.net/suamikim/auXdQ/

There are a few things i don't like about this solution:

  1. The override changes (per definition...) the behaviour of all DropZones in my application. How can i only override/extend the specific DropZone of one grid?

    I've tried the following:

    • Add an interceptor to the dropZone after the gridview has been rendered: http://jsfiddle.net/suamikim/uv8tX/

      At first this seems to work because it shows the correct drop-allowed-indication but it drops the record even if the indicator shows that it's not allowed (it always shows the "green line"...)

    • Define a new dnd-plugin which extends the treeviewdragdrop-plugin and just override the onNodeOver-method of the dropZone after it's creation: http://jsfiddle.net/suamikim/5v67W/

      This kind of does the opposite from the interception-method. It also shows the correct indication but it never shows the "green line" and won't allow the drop anywhere...

  2. The class i'm overriding (Ext.view.DropZone) is marked private in the documentation with a note that it shouldn't be used directly...

I would really appreciate some comments on those 2 issues and maybe even some better solutions!

Thanks, mik


Edit:

I adjusted the version in which i defined a new dnd-plugin which extended the original gridviewdragdrop-plugin. The "magic" was to also extend gridviewdropzone and extend the onNodeOver-method instead of just overriding it.

This needs to be done because the original onNodeOver-method which is now called by callParent handles the "green line" and finally allows the drop.

The only thing my extended gridviewdragdrop-plugin does now is to create a instance of the new dropzone-class instead of the standard gridviewdropzone in the onViewRender-method.
This seems like a reasonable way so far:

// Extend the treeview dropzone
Ext.define('ExtendedGridViewDropZone', {
    extend: 'Ext.grid.ViewDropZone',

    onNodeOver: function(nodeData, source, e, data) {
        if (data && data.records && data.records[0]) {
            // The check should be specified, e.g. a female with the name 'Malena' would be recognized as male!
            if (nodeData.innerHTML.indexOf(data.records[0].get('sex')) < 0) {
                    return this.dropNotAllowed;
            }
        }

        return this.callParent(arguments);
    },
    onContainerOver: function(source, e, data) {
        return this.dropNotAllowed;
    }
});

Ext.define('ExtendedGridDnD', {
    extend: 'Ext.grid.plugin.DragDrop',
    alias: 'plugin.extendeddnd',

    onViewRender: function(view) {
        this.callParent(arguments);

        // Create a instance of ExtendedGridViewDropZone instead of Ext.grid.ViewDropZone
        this.dropZone = Ext.create('ExtendedGridViewDropZone', {
            view: view,
            ddGroup: this.dropGroup || this.ddGroup
        });
    }
});

Working example: http://jsfiddle.net/5v67W/1/

Nonetheless I'd still appreciate different approaches because it still feels like it could be done easier...

Comments