View Full Version : Drag and drop on LiveGrid not working

28 Jan 2016, 9:24 AM
I had the following code working in GXT 3.1.4

new GridDragSource<T>(grd);
GridDropTarget<T> drpTrgt = new GridDropTarget<T>(grd);
drpTrgt.addDropHandler(new DndDropHandler() {

public void onDrop(DndDropEvent event) {

After upgrading to GXT 4, it lets me drag, but I can't drop. It behaves like setAllowSelfAsSource(false).

My grid is using LiveGridView, and I am using DndDropHandler to handle moving the item on server-side and reloading the view, which shouldn't affect the dnd behavior on client side.

28 Jan 2016, 11:59 AM
Looks like it may be caused by a change in LiveGridView scroller implementation.

In GripDropTarget.onDragMove, the following condition is always true for LiveGridView, setting drop allowed status to false:

if (Element.is(target) && !grid.getView().getScroller().isOrHasChild(Element.as(target))) {

I override it with:

GridDropTarget<T> drpTrgt = new GridDropTarget<T>(grd) {
protected void onDragMove(DndDragMoveEvent event) {
Element target = getElementFromEvent(event.getDragMoveEvent().getNativeEvent());

if (Element.is(target) && !(grid.getView() instanceof LiveGridView)
&& !grid.getView().getScroller().isOrHasChild(Element.as(target))) {


Which works for me, but not sure if it's correct.

Not sure if it's bug. Could someone clarify?

I think LiveGridView needs better documentation and support. Because it extends GridVew, it seems to support a lot of grid features, such as DnD. But over the years I have learned it has a lot of gotchas, such as:
- fixed row height
- couldn't handle touch scroll (until GXT 4)
- DnD doesn't auto scroll.

Would be nice if the documentation warns user about some of the gotchas.

5 Feb 2016, 12:54 PM
I worked up a test case to test drag and drop with the live grid. I'm not sure I understand the issue quite yet.

This allows me to drag and drop into the live grid. There is an issue with this mock up, is that it clobbers what I add if it pages through the data because it doesn't get persisted. I mocked up a server side code. But I think this might help provide some hints, or it will help me sync up with what you're experiencing.

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.event.logical.shared.AttachEvent.Handler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.core.client.util.Margins;
import com.sencha.gxt.data.client.loader.RpcProxy;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.ModelKeyProvider;
import com.sencha.gxt.data.shared.loader.FilterPagingLoadConfig;
import com.sencha.gxt.data.shared.loader.FilterPagingLoadConfigBean;
import com.sencha.gxt.data.shared.loader.LoadResultListStoreBinding;
import com.sencha.gxt.data.shared.loader.PagingLoadResult;
import com.sencha.gxt.data.shared.loader.PagingLoader;
import com.sencha.gxt.dnd.core.client.GridDragSource;
import com.sencha.gxt.dnd.core.client.GridDropTarget;
import com.sencha.gxt.dnd.core.client.DND.Feedback;
import com.sencha.gxt.widget.core.client.ContentPanel;
import com.sencha.gxt.widget.core.client.container.BorderLayoutContainer;
import com.sencha.gxt.widget.core.client.container.BorderLayoutContainer.BorderLayoutData;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;
import com.sencha.gxt.widget.core.client.grid.LiveGridView;
import com.sencha.gxt.widget.core.client.grid.filters.StringFilter;

public class LiveGridDndExample implements IsWidget, EntryPoint {

protected static final int MIN_HEIGHT = 450;
protected static final int MIN_WIDTH = 450;

private VerticalLayoutContainer panel;

private static final int COLUMNS_SIZE = 5;
private static final int TOTAL_LENGTH = 300;

private PagingLoader<FilterPagingLoadConfig, PagingLoadResult<Data>> pagingLoader1;
private PagingLoader<FilterPagingLoadConfig, PagingLoadResult<Data>> pagingLoader2;
private Grid<Data> grid1;
private Grid<Data> grid2;

public Widget asWidget() {
if (panel == null) {
panel = new VerticalLayoutContainer();
panel.add(createGrids1And2(), new VerticalLayoutData(1, 0.5, new Margins(0, 0, 10, 0)));

return panel;

private ContentPanel createGrids1And2() {
ListStore<Data> listStore1 = getStore1();
ListStore<Data> listStore2 = getStore2();

LiveGridView<Data> liveGridView1 = new LiveGridView<Data>();
LiveGridView<Data> liveGridView2 = new LiveGridView<Data>();

ColumnModel<Data> cm1 = new ColumnModel<Data>(getColumns());
ColumnModel<Data> cm2 = new ColumnModel<Data>(getColumns());

pagingLoader1 = getLoader(listStore1, false);
pagingLoader2 = getLoader(listStore2, true);

grid1 = new Grid<Data>(listStore1, cm1, liveGridView1);
grid1.addAttachHandler(new Handler() {
public void onAttachOrDetach(AttachEvent event) {

grid2 = new Grid<Data>(listStore2, cm2, liveGridView2);
grid2.addAttachHandler(new Handler() {
public void onAttachOrDetach(AttachEvent event) {

GridDragSource<Data> gds1 = new GridDragSource<Data>(grid1);
GridDragSource<Data> gds2 = new GridDragSource<Data>(grid2);


GridDropTarget<Data> gdt1 = new GridDropTarget<Data>(grid1);
GridDropTarget<Data> gdt2 = new GridDropTarget<Data>(grid2);



BorderLayoutData westData = new BorderLayoutData(0.5);
westData.setMargins(new Margins(0, 2, 0, 0));

BorderLayoutData centerData = new BorderLayoutData();
centerData.setMargins(new Margins(0, 0, 0, 2));

BorderLayoutContainer borderLayoutContainer = new BorderLayoutContainer();
borderLayoutContainer.setWestWidget(grid1, westData);
borderLayoutContainer.setCenterWidget(grid2, centerData);

ContentPanel panel = new ContentPanel();
panel.setHeadingText("Grid to Grid Insert at Sort Position");

return panel;

private ListStore<Data> getStore1() {
ListStore<Data> store = new ListStore<Data>(new ModelKeyProvider<Data>() {
public String getKey(Data item) {
return "a" + item.getKey();
return store;

private ListStore<Data> getStore2() {
ListStore<Data> store = new ListStore<Data>(new ModelKeyProvider<Data>() {
public String getKey(Data item) {
return "b" + item.getKey();
return store;

public void onModuleLoad() {
ContentPanel cp = new ContentPanel();
cp.setPixelSize(700, 500);


private List<ColumnConfig<Data, ?>> getColumns() {
List<ColumnConfig<Data, ?>> columns = new ArrayList<ColumnConfig<Data, ?>>();

for (int c = 0; c < COLUMNS_SIZE; c++) {
String header = "col" + c;
ColumnConfig<Data, String> col = new ColumnConfig<Data, String>(new ValueProviderExt(c), 70, header);

return columns;

private PagingLoader<FilterPagingLoadConfig, PagingLoadResult<Data>> getLoader(ListStore<Data> store,
final boolean grid1OrGrid2) {
RpcProxy<FilterPagingLoadConfig, PagingLoadResult<Data>> proxy = new RpcProxy<FilterPagingLoadConfig, PagingLoadResult<Data>>() {
public void load(FilterPagingLoadConfig loadConfig, AsyncCallback<PagingLoadResult<Data>> callback) {
getDatas(loadConfig, callback, grid1OrGrid2);

PagingLoader<FilterPagingLoadConfig, PagingLoadResult<Data>> loader = new PagingLoader<FilterPagingLoadConfig, PagingLoadResult<Data>>(
loader.useLoadConfig(new FilterPagingLoadConfigBean());
loader.addLoadHandler(new LoadResultListStoreBinding<FilterPagingLoadConfig, Data, PagingLoadResult<Data>>(store));

return loader;

private void getDatas(FilterPagingLoadConfig loadConfig, AsyncCallback<PagingLoadResult<Data>> callback,
boolean grid1OrGrid2) {
final int offset = loadConfig.getOffset();
int limit = loadConfig.getLimit();

System.out.println("getDatas: offset=" + offset + " limit=" + limit);

final List<Data> datas = new ArrayList<Data>();
for (int i = offset; i < offset + limit; i++) {
datas.add(getData(i, grid1OrGrid2));

PagingLoadResult<Data> result = new PagingLoadResult<Data>() {
public List<Data> getData() {
return datas;

public void setTotalLength(int totalLength) {

public void setOffset(int offset) {

public int getTotalLength() {

public int getOffset() {
return offset;

private Data getData(int row, boolean grid1OrGrid2) {
String aOrB = "~~A:";
if (grid1OrGrid2) {
aOrB = "B:";

String key = aOrB + "key" + row;

String[] values = new String[COLUMNS_SIZE];
for (int col = 0; col < COLUMNS_SIZE; col++) {
values[col] = aOrB + col + "," + row;

Data data = new Data(key, values);
return data;

public class ValueProviderExt implements ValueProvider<Data, String> {
private int index;

public ValueProviderExt(int index) {
this.index = index;

public String getValue(Data data) {
return data.getValue(index);

public void setValue(Data object, String value) {

public String getPath() {
return "path" + index;

public class StringFilterExt extends StringFilter<Data> {
public StringFilterExt(int index) {
super(new ValueProviderExt(index));

public class Data {
private String key;
private String[] values;

public Data(String key, String[] values) {
this.key = key;
this.values = values;

public String getKey() {
return key;

public void setKey(String key) {
this.key = key;

public String getValue(int index) {
return values[index];

public void setValue(int index, String value) {
this.values[index] = value;

public String toString() {
String s = "Data(";
s += "key=" + key;
s += ")";
return s;

Could you help mock the issue in the test case and return it? And then provide the steps of the issue to help me understand how to reproduce it.

Thanks for the tips on the guides. I'll add some todos for that too.


9 Feb 2016, 5:27 AM
From what I can tell, the test case you have is a Grid to Grid DnD.

In my case, I have only one grid, and I want to reorder an item in the grid by DnD (move it up or down). Hence setAllowSelfAsSource(true);

The idea is: on drop, I figure out the item's new position relative to the other items before and after it, and update the server side position and then reload the grid. That's not an issue.

The issue is I can drag an item, but it won't let me drop it on the same grid. From what I can tell, the if statement in onDragMove method evaluates to true. So it sets the StatusProxy to false, preventing the drop.

I am not sure what the if statement is checking, but my workaround is to bypass the check if View is LiveGridView - which I admit hacky.

Really the issue is setAllowSelfAsSource(true); is not behaving as expected. It worked in GXT 3.1.4.