Redraw CellTable from MainPresenter after popup view is hidden - gwt

My MainPresenter has a CellTable with a button column. When u hit a button the presenter calls "addToPopupSlot(editPopup, true)". A editPopup appears with several settings u can make there. After pressing the save button on the popup view it sends data to the database which the CellTable in the MainPresenter wants to get.
My problem is: When I click on the save button, the table doesnt get updated. I have to either refresh the page or navigate from another Presenter back to the MainPresenter.
EditPopupPresenter
#Override
protected void onBind() {
super.onBind();
this.username = Cookies.getCookie("domusr");
// hours and minutes displayed in listboxes
for (int i = 0; i < TimeSettings.HOURS_RANGE; i++) {
getView().getBeginHoursLBX().addItem(String.valueOf(i));
getView().getEndHoursLBX().addItem(String.valueOf(i));
getView().getPauseHoursLBX().addItem(String.valueOf(i));
}
for (int i = 0; i < 60; i += TimeSettings.MINUTES_RANGE) {
getView().getBeginMinutesLBX().addItem(String.valueOf(i));
getView().getEndMinutesLBX().addItem(String.valueOf(i));
getView().getPauseMinutesLBX().addItem(String.valueOf(i));
}
getView().getSaveBTN().addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
DateTimeFormat dtf = DateTimeFormat.getFormat("yyyy-MM-dd");
final String startHours = getView()
.getBeginHoursLBX()
.getValue(
getView().getBeginHoursLBX().getSelectedIndex());
final String startMinutes = getView().getBeginMinutesLBX()
.getValue(
getView().getBeginMinutesLBX()
.getSelectedIndex());
final String endHours = getView().getEndHoursLBX().getValue(
getView().getEndHoursLBX().getSelectedIndex());
final String endMinutes = getView()
.getEndMinutesLBX()
.getValue(
getView().getEndMinutesLBX().getSelectedIndex());
final String pauseHours = getView()
.getPauseHoursLBX()
.getValue(
getView().getPauseHoursLBX().getSelectedIndex());
final String pauseMinutes = getView().getPauseMinutesLBX()
.getValue(
getView().getPauseMinutesLBX()
.getSelectedIndex());
final String projectId = getView().getProjectIdLBL().getText();
final java.sql.Date date = new java.sql.Date(dtf.parse(
getView().getDateLBL().getText()).getTime());
dispatcher.execute(
new InsertTimesIntoDB(Integer.parseInt(startHours),
Integer.parseInt(startMinutes), Integer
.parseInt(endHours), Integer
.parseInt(endMinutes), Integer
.parseInt(pauseHours), Integer
.parseInt(pauseMinutes), Integer
.parseInt(projectId), date, username),
new AsyncCallback<InsertTimesIntoDBResult>() {
#Override
public void onFailure(Throwable caught) {
}
#Override
public void onSuccess(InsertTimesIntoDBResult result) {
}
});
getView().hide();
}
});
}
editColumn in MainPresenter (onBind())
// edit column
Column<Booking, String> editColumn = new Column<Booking, String>(
new ButtonCell()) {
#Override
public String getValue(Booking booking) {
return "edit";
}
};
editColumn.setFieldUpdater(new FieldUpdater<Booking, String>() {
#Override
public void update(int index, Booking object, String value) {
// pop up widget addToSlot call
editPopup.getView().getDateLBL()
.setText(String.valueOf(object.getFullDate()));
editPopup.getView().getProjectIdLBL()
.setText(String.valueOf(1234567));
editPopup.getView().getBeginHoursLBX()
.setItemSelected(object.getStartHours(), true);
editPopup
.getView()
.getBeginMinutesLBX()
.setItemText(
minutesRange.getIndex(object
.getEndMinutes()),
String.valueOf(object.getStartMinutes()));
editPopup.getView().getEndHoursLBX()
.setItemSelected(object.getEndHours(), true);
editPopup
.getView()
.getEndMinutesLBX()
.setItemText(
minutesRange.getIndex(object
.getEndMinutes()),
String.valueOf(object.getEndMinutes()));
editPopup.getView().getPauseHoursLBX()
.setItemSelected(object.getPauseHours(), true);
editPopup
.getView()
.getPauseMinutesLBX()
.setItemText(
minutesRange.getIndex(object
.getEndMinutes()),
String.valueOf(object.getPauseMinutes()));
addToPopupSlot(editPopup, true);
}
});
getView().getTimeTable().addColumn(editColumn);

I think you have some solutions here. If I were you I would do next steps:
Create a listener of events in the MainPresenter.
When you finished
update your DB (after pressing save in your popup); I´d fire an
event.
When the MainPresenter receives the event, you go to the DB
and fetch the data (filtering it using getVisibleRange()).
Refresh the CellTable using setRowData(...) method (passing correctly the arguments)
Other option is create a ListDataProvider associate with the CellTable, and call refresh on it.

Related

CellTable click swallowed

I've an combo box which is composed of a text field and a popup with a CellTable showing the suggestion items. The text field has a change handler that updates the CellTable's selection.
When typing a character and clicking an already selected suggestion, the first click is swallowed. The second click works and triggers the selection via the CellTable.addDomHandler(...).
Any idea why first click is swallowed?
Example code:
private static class SuggestFieldTextAndPopupSandbox extends SimplePanel {
private final TextField mText;
private CellTable<Handle<String>> mTable;
private SingleSelectionModel<Handle<String>> mTableSelection;
private SingleSelectionModel<Handle<String>> mSelection;
private ProvidesKey<Handle<String>> mKeyProvider = new SimpleKeyProvider<Handle<String>>();
private PopupPanel mPopup;
private List<Handle<String>> mData;
public SuggestFieldTextAndPopupSandbox() {
mData = Lists.newArrayList(new Handle<String>("AAA"), new Handle<String>("AAB"), new Handle<String>("ABB"));
mSelection = new SingleSelectionModel<Handle<String>>();
mText = new TextField();
mText.addKeyPressHandler(new KeyPressHandler() {
#Override
public void onKeyPress(KeyPressEvent pEvent) {
mPopup.showRelativeTo(mText);
}
});
mText.addBlurHandler(new BlurHandler() {
#Override
public void onBlur(BlurEvent pEvent) {
mTableSelection.setSelected(startsWith(mText.getValue()), true);
}
});
mText.addChangeHandler(new ChangeHandler() {
#Override
public void onChange(ChangeEvent pEvent) {
mText.setText(mText.getText().toUpperCase());
}
});
mTable = new CellTable<Handle<String>>(0, GWT.<TableResources>create(TableResources.class));
mTable.setTableLayoutFixed(false);
mTableSelection = new SingleSelectionModel<Handle<String>>(mKeyProvider);
mTable.setSelectionModel(mTableSelection);
mTable.addDomHandler(new ClickHandler() {
#Override
public void onClick(final ClickEvent pEvent) {
Scheduler.get().scheduleFinally(new ScheduledCommand() {
#Override
public void execute() {
mSelection.setSelected(mTableSelection.getSelectedObject(), true);
mText.setFocus(true);
mPopup.hide();
}
});
}
}, ClickEvent.getType());
mTable.addColumn(new TextColumn<Handle<String>>() {
#Override
public String getValue(Handle<String> pObject) {
return pObject.get();
}
});
mTable.setRowData(mData);
mPopup = new PopupPanel();
mPopup.setAutoHideEnabled(true);
mPopup.setWidget(mTable);
mPopup.setWidth("200px");
mPopup.setHeight("200px");
VerticalPanel p = new VerticalPanel();
p.add(mText);
setWidget(p);
}
private Handle<String> startsWith(final String pValue) {
final String val = nullToEmpty(pValue).toLowerCase();
int i = 0;
for (Handle<String> item : mData) {
String value = item.get();
if (value != null && value.toLowerCase().startsWith(val)) {
return item;
}
i++;
}
return null;
}
}
I reproduced your issue and here is the problem:
when you click on the suggestions the following is happening:
The text field is loosing focus which causes the corresponding ChangeEvent to be dealt with followed by the BlurEvent.
The click causes the popup to get the focus now which is why it is swallowed.
If you remove the ChangeHandler and the BlurHandler of the text field the issue disappears. But I think I found another solution
Try replacing the DOM handler of the mTable with a selection handler relative to the mTableSelection as follows:
mTableSelection.addSelectionChangeHandler(new Handler(){
#Override
public void onSelectionChange(SelectionChangeEvent event) {
Scheduler.get().scheduleFinally(new ScheduledCommand() {
#Override
public void execute() {
mSelection.setSelected(mTableSelection.getSelectedObject(), true);
mText.setFocus(true);
mPopup.hide();
}
});
}
});
Found a way how to properly solve this.
Skipping the blur handler when user hovers the suggestion list area seemed to fix that issue, at least from the tests that were done didn't see any more issues.
This was necessary because just before the user clicks a suggestion item, the text is blurred and it fires a selection change. This in turn cancels the selection made when user clicks an item.

Unable to get GWT ListDataProvider to work with DataGrid

I had my data in a FlexTable, but am migrating to a DataGrid so I can easily add pagination. I get the data via a REST call. I can't seem to get the data to actually display. Here are the relevant snippets:
private DataGrid<SearchResult> resultsGrid = new DataGrid<SearchResult>();
resultsGrid.setAutoHeaderRefreshDisabled(true);
TextColumn<SearchResult> titleColumn = new TextColumn<SearchResult>() {
#Override
public String getValue(SearchResult object) {
return object.getTitle();
}
};
resultsGrid.addColumn(titleColumn, "Document Title");
ButtonCell buttonCell = new ButtonCell();
Column<SearchResult, String> buttonColumn = new Column<SearchResult, String>(buttonCell){
#Override
public String getValue(SearchResult object) {
return "Show";
}
};
resultsGrid.addColumn(buttonColumn, "");
buttonColumn.setFieldUpdater(new FieldUpdater<SearchResult, String>() {
public void update(int index, SearchResult object, String value) {
doPreview(object.title);
}
});
TextColumn<SearchResult> roleColumn = new TextColumn<SearchResult>() {
#Override
public String getValue(SearchResult object) {
return object.getRoles();
}
#Override
public String getCellStyleNames(Context context, SearchResult object) {
if (object.containsCurrentRole)
return "highlight";
else
return null;
}
};
resultsGrid.addColumn(roleColumn, "Associated Roles");
final SingleSelectionModel<SearchResult> selectionModel = new SingleSelectionModel<SearchResult>();
resultsGrid.setSelectionModel(selectionModel);
selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
public void onSelectionChange(SelectionChangeEvent event) {
SearchResult selected = selectionModel.getSelectedObject();
if (selected != null) {
clearWordCloud();
getWordCloud(selected.getTitle());
}
}
});
dataProvider.addDataDisplay(resultsGrid);
// Create a Pager to control the table.
SimplePager.Resources pagerResources = GWT.create(SimplePager.Resources.class);
pager = new SimplePager(TextLocation.CENTER, pagerResources, false, 0, true);
pager.setDisplay(resultsGrid);
resultsGrid.setVisible(true);
resultsGrid.addStyleName("results");
mainPanel.add(resultsGrid);
...
The function that gets called after a search:
private void updateTable(List<SearchResult> results) {
dataProvider.getList().addAll(results);
dataProvider.refresh();
dataProvider.flush();
resultsGrid.setVisible(true);
resultsFlexTable.setVisible(true);
}
At first I was missing the flush and refresh, but adding them had no effect. I'm kind of stumped.
The most likely problem is that your DataGrid has a height of zero. DataGrid implements RequiresResize, which means that its height either has to be set explicitly, or it will acquire its height from a parent widget if this parent widget implements ProvidesResize. FlexTable does not implement ProvidesResize interface.
NB: You don't need flush and refresh - adding data to the DataProvider refreshes the grid.

Why can't I use GWT Button in a CellTable and how to combine cell buttons with cell text

I want to put some text and severals Buttons in a CellTable. I can display the text and buttons, but when I click on a button, nothing happens.
final SafeHtmlCell detailsCell = new SafeHtmlCell();
Column<UIRow, SafeHtml> detailsColumn = new Column<UIRow, SafeHtml>(
detailsCell) {
#Override
public SafeHtml getValue(UIRow object) {
String details = "some informations xxx <br/>";
Button addButton = new Button("Add value", new ClickHandler() {
public void onClick(ClickEvent event) {
Window.alert("hello");
}
});
details += addButton;
SafeHtmlBuilder sb = new SafeHtmlBuilder();
sb.appendHtmlConstant(details);
return sb.toSafeHtml();
}
};
detailsColumn.setCellStyleNames(CSS_DETAILS_TD);
table.setColumnWidth(detailsColumn, "10%");
table.addColumn(detailsColumn, new SafeHtmlHeader(SafeHtmlUtils.fromSafeConstant("details")));
But, if I put a Button per Cell, it's working, onClick method is called:
final ButtonCell btnCell = new ButtonCell() {
#Override
public void render(
final Context context,
final SafeHtml data,
final SafeHtmlBuilder sb) {
sb.appendHtmlConstant("<button type=\"button\" style=\"height: 25px\" title=\"submit\" class=\"btn btn-success btn-small\" tabindex=\"-1\">");
if (data != null) {
sb.append(data);
}
sb.appendHtmlConstant("</button>");
}
};
Column<UIRow, String> btnColumn = new Column<UIRow, String>(btnCell) {
#Override
public String getValue(UIRow object) {
return "";
}
};
btnColumn.setCellStyleNames("td-actions");
table.addColumn(btnColumn, "");
table.setColumnWidth(btnColumn, "5%");
btnColumn.setFieldUpdater(new FieldUpdater<UIRow, String>() {
public void update(int index, UIRow object, String value) {
...
service.submit(object, false, new AsyncCallback<String>() {
#Override
public void onFailure(Throwable caught) {
Window.alert(caught.getMessage());
}
#Override
public void onSuccess(String result) {
Window.alert(result);
}
});
}
});
}
I want to display Html text and Buttons in one Col/Cell, is it possible ?
Thanks
Cell widgets (CellTable, DataGrid, etc) are not designed to embed regular GWT widgets like Button.
For buttons you can use ButtonCell
Take a look at official example:
http://samples.gwtproject.org/samples/Showcase/Showcase.html#!CwCellSampler
Update:
As for the second part of the question regarding how to combine cells, there is a few options:
Check out CompositeCell where you can combine multiple cells (eg ButtonCell and TextCell).
Another option is to use custom CellTableBuilder where you can customize all aspects of table rendering like colspans, column/row decorations, etc

Gwt Simple pager issues with a column sort handler

I have set up an AsyncDataProvider for my CellTable and added it to a SimplePager. I have hooked up a ListHandler to take care of sorting based on a column.
When I click the header of that column, the data doesn't change but on going to the next/previous page within the pager the data is then sorted. Also before the column is clicked there is no visual indicator on the column that would indicate that it is meant to be sortable.
How can I get the data to update when I click the header of the Column?
Here's my code snippet
service.getHosts(environment, new AsyncCallback<Set<String>>() {
#Override
public void onSuccess(final Set<String> hosts) {
final List<String> hostList = new ArrayList<String>(hosts);
//Populate the table
CellTable<String> hostTable = new CellTable<String>();
TextColumn<String> hostNameColumn = new TextColumn<String>(){
#Override
public String getValue(String string){
return string;
}
};
NumberCell numberCell = new NumberCell();
Column<String, Number> lengthColumn = new Column<String, Number>(numberCell){
#Override
public Number getValue(String string) {
return new Integer(string.length());
}
};
AsyncDataProvider<String> dataProvider = new AsyncDataProvider<String>() {
#Override
protected void onRangeChanged(HasData<String> data) {
int start = data.getVisibleRange().getStart();
int end = start + data.getVisibleRange().getLength();
List<String> subList = hostList.subList(start, end);
updateRowData(start, subList);
}
};
// Hooking up sorting
ListHandler<String> columnSortHandler = new ListHandler<String>(hostList);
columnSortHandler.setComparator(lengthColumn, new Comparator<String>(){
#Override
public int compare(String arg0, String arg1) {
return new Integer(arg0.length()).compareTo(arg1.length());
}
});
hostTable.setPageSize(10);
hostTable.addColumnSortHandler(columnSortHandler);
hostTable.addColumn(hostNameColumn,"Host Name");
lengthColumn.setSortable(true);
hostTable.addColumn(lengthColumn, "Length");
VerticalPanel verticalPanel = new VerticalPanel();
SimplePager pager = new SimplePager();
pager.setDisplay(hostTable);
dataProvider.addDataDisplay(hostTable);
dataProvider.updateRowCount(hosts.size(), true);
verticalPanel.add(hostTable);
verticalPanel.add(pager);
RootPanel.get().add(verticalPanel);
}
#Override
public void onFailure(Throwable throwable) {
Window.alert(throwable.getMessage());
}
});
I'm not sure how to make sure that the list is shared by both the table and the Pager. Before adding the pager I was using
ListDataProvider<String> dataProvider = new ListDataProvider<String>();
ListHandler<String> columnSortHandler = new ListHandler<String>(dataProvider.getList());
The AsyncDataProvider doesn't have the method getList.
To summarize I want the data to be sorted as soon as the column is clicked and not after I move forward/backward with the pager controls.
As per the suggestion I have changed the code for the AsyncDataProvider to
AsyncDataProvider<String> dataProvider = new AsyncDataProvider<String>() {
#Override
protected void onRangeChanged(HasData<String> data) {
int start = data.getVisibleRange().getStart();
int end = start + data.getVisibleRange().getLength();
List<String> subList = hostList.subList(start, end);
// Hooking up sorting
ListHandler<String> columnSortHandler = new ListHandler<String>(hostList);
hostTable.addColumnSortHandler(columnSortHandler);
columnSortHandler.setComparator(lengthColumn, new Comparator<String>(){
#Override
public int compare(String v0, String v1) {
return new Integer(v0.length).compareTo(v1.length);
}
});
updateRowData(start, subList);
}
};
But there is no change in the behavior even after that. Can someone please explain the process. The GWT showcase app seems to have this functionality but how they've done it isn't all that clear.
When using an AsyncDataProvider both pagination and sorting are meant to be done on the server side. You will need an AsyncHandler to go with your AsyncDataProvider:
AsyncHandler columnSortHandler = new AsyncHandler(dataGrid) {
#Override
public void onColumnSort(ColumnSortEvent event) {
#SuppressWarnings("unchecked")
int sortIndex = dataGrid.getColumnIndex((Column<Entry, ?>) event.getColumn());
boolean isAscending = event.isSortAscending();
service.getPage(0, sortIndex, isAscending, new AsyncCallback<List<Entry>>() {
public void onFailure(Throwable caught) {
}
public void onSuccess(List<Entry> result) {
pager.setPage(0);
provider.updateRowData(0, result);
}
});
}
};
dataGrid.addColumnSortHandler(columnSortHandler);
Clicking on a column header will then fire a columnSortEvent. Then you have to get the column clicked. I am overloading my servlet to provide both sorting and pagination, so I pass a -1 for the column index when only pagination is desired.
provider = new AsyncDataProvider<Entry>() {
#Override
protected void onRangeChanged(HasData<Entry> display) {
final int start = display.getVisibleRange().getStart();
service.getPage(start, -1, true, new AsyncCallback<List<Entry>>() {
#Override
public void onFailure(Throwable caught) {
}
#Override
public void onSuccess(List<Entry> result) {
provider.updateRowData(start, result);
}
});
}
};
provider.addDataDisplay(dataGrid);
provider.updateRowCount(0, true);
Then your servlet implementation of getPage performs the sorting and pagination. The whole thing is much easier to follow with separate event handlers.
I think the problem is with the ListHandler initialization. You are passing hostList as a parameter to List Handler and in onRangeChange method you are calling updateRowData with a different list (sublist).
Make sure you use the same list in both the places.
or
Move your ListHander initialization and cellTable.addColumnSortHandler method call to onRangeChange method after updateRowData call.

Attempt to set model object on null model of component: form:checkgroup

I'm trying to create a list of HITs (objects), where each has a checkbox next to it, so that I can select them and delete them all at once. I've made a form with a checkbox for each row in the table:
final HashSet<HIT> selectedValues = new HashSet<HIT>();
final CheckGroup checkgroup = new CheckGroup("checkgroup");
final Form form = new Form("form"){
#Override
public void onSubmit() {
super.onSubmit();
}
};
checkgroup.add(new CheckGroupSelector("checkboxSelectAll"));
UserHitDataProvider userHitDataProvider = new UserHitDataProvider(selectedIsReal, keyId, secretId);
final DataView<HIT> dataView = new DataView<HIT>("pageable", userHitDataProvider) {
private static final long serialVersionUID = 1L;
#Override
protected void populateItem(final Item<HIT> item) {
HIT hit = item.getModelObject();
item.add(new CheckBox("checkbox", new SelectItemUsingCheckboxModel(hit,selectedValues)));
item.add(new Label("hitName", String.valueOf(hit.getTitle())));
item.add(new Label("hitId", String.valueOf(hit.getHITId())));
}
};
//add checkgroup to form, form to page, etc.
I've also added a new class to take care of the selection/deletion:
public class SelectItemUsingCheckboxModel extends AbstractCheckBoxModel {
private HIT hit;
private Set selection;
public SelectItemUsingCheckboxModel(HIT h, Set selection) {
this.hit = h;
this.selection = selection;
}
#Override
public boolean isSelected() {
return selection.contains(hit);
}
#Override
public void select() {
selection.add(hit);
}
#Override
public void unselect() {
selection.remove(hit);
}
}
Everything renders fine, but I get an error when trying to submit:
Caused by: java.lang.IllegalStateException: Attempt to set model object on null model of component: form:checkgroup
at org.apache.wicket.Component.setDefaultModelObject(Component.java:3042)
at org.apache.wicket.markup.html.form.FormComponent.updateCollectionModel(FormComponent.java:1572)
at org.apache.wicket.markup.html.form.CheckGroup.updateModel(CheckGroup.java:160)
at org.apache.wicket.markup.html.form.Form$FormModelUpdateVisitor.component(Form.java:228)
at org.apache.wicket.markup.html.form.Form$FormModelUpdateVisitor.component(Form.java:198)
at org.apache.wicket.util.visit.Visits.visitPostOrderHelper(Visits.java:274)
at org.apache.wicket.util.visit.Visits.visitPostOrderHelper(Visits.java:262)
at org.apache.wicket.util.visit.Visits.visitPostOrder(Visits.java:245)
at org.apache.wicket.markup.html.form.FormComponent.visitComponentsPostOrder(FormComponent.java:422)
at org.apache.wicket.markup.html.form.Form.internalUpdateFormComponentModels(Form.java:1793)
at org.apache.wicket.markup.html.form.Form.updateFormComponentModels(Form.java:1757)
at org.apache.wicket.markup.html.form.Form.process(Form.java:913)
at org.apache.wicket.markup.html.form.Form.onFormSubmitted(Form.java:770)
at org.apache.wicket.markup.html.form.Form.onFormSubmitted(Form.java:703)
... 27 more
I think its some of the Ajax code breaking, since my SelectAllCheckBox button is also failing. Any ideas why? Is this even the best way to handle such a use case?
Your Checkgroup does not have a Model, thus Wicket can't copy the current state of the Model into a null object. You should use the constructor with an additional parameter representing the Model you want to store the value in.