PDA

View Full Version : Drag and drop on LiveGrid not working



iyeh
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.setAllowSelfAsSource(true);
drpTrgt.setFeedback(Feedback.INSERT);
drpTrgt.addDropHandler(new DndDropHandler() {


@SuppressWarnings("unchecked")
@Override
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.

iyeh
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))) {
event.setCancelled(true);
event.getStatusProxy().setStatus(false);
return;
}


I override it with:



GridDropTarget<T> drpTrgt = new GridDropTarget<T>(grd) {
@Override
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))) {
event.setCancelled(true);
event.getStatusProxy().setStatus(false);
return;
}


event.setCancelled(false);
event.getStatusProxy().setStatus(true);
}
};


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.

branflake2267
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;


@Override
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.setLoader(pagingLoader1);
grid1.addAttachHandler(new Handler() {
@Override
public void onAttachOrDetach(AttachEvent event) {
pagingLoader1.load();
}
});


grid2 = new Grid<Data>(listStore2, cm2, liveGridView2);
grid2.setLoader(pagingLoader2);
grid2.addAttachHandler(new Handler() {
@Override
public void onAttachOrDetach(AttachEvent event) {
pagingLoader2.load();
}
});


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

gds1.setGroup("top");
gds2.setGroup("top");


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

gdt1.setGroup("top");
gdt2.setGroup("top");

gdt1.setFeedback(Feedback.INSERT);
gdt2.setFeedback(Feedback.INSERT);


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");
panel.add(borderLayoutContainer);


return panel;
}


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


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


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


RootPanel.get().add(cp);
}


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);
col.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);


columns.add(col);
}
return columns;
}


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


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


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>() {
@Override
public List<Data> getData() {
return datas;
}


@Override
public void setTotalLength(int totalLength) {
}


@Override
public void setOffset(int offset) {
}


@Override
public int getTotalLength() {
return TOTAL_LENGTH;
}


@Override
public int getOffset() {
return offset;
}
};
callback.onSuccess(result);
}


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;
}


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


@Override
public void setValue(Data object, String value) {
}


@Override
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;
}


@Override
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.

Thanks,
Brandon

iyeh
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.

Thanks.