Wicket children panels refresh - wicket

I'm just looking for extra pair of eyes to spot why children panels do not show up/change visibility on radio button selection/change that does call for their refresh
public class OptionsPanel extends Panel {
private AutoCompleteSearchField departureField;
private HiddenField departureCodeField;
private CompoundPropertyModel model;
private RadioGroup flightChoices;
private RadioGroup dateChoices;
private final TripSearchModel tripSearchModel;
private WebMarkupContainer optionsContainer;
private FlexibleDates flexibleDates;
private FixedDates fixedDates;
private PassengersAndClass passengersAndClass;
private static List FIX_CONTAINER_VISIBLE = Lists.newArrayList(true, false, true);
private static List FLEX_CONTAINER_VISIBLE = Lists.newArrayList(false, true, true);
private static List HIDE_CONTAINERS = Lists.newArrayList(false, false, false);
public OptionsPanel(String id, CompoundPropertyModel model) {
super(id);
this.model = model;
this.tripSearchModel = (TripSearchModel) model.getObject();
add(departureLabel());
add(departureField());
add(departureCodeField());
add(flightType());
add(travellingWhen());
add(dateType());
add(optionsContainer(HIDE_CONTAINERS));
}
private Component departureLabel() {
return new WebMarkupContainer("departureLabel").setOutputMarkupId(true);
}
private AutoCompleteSearchField departureField() {
departureField = new AutoCompleteSearchField("departureField", "From", "flightFromField", null, true, model.bind("departure"));
departureField.setOutputMarkupId(true);
departureField.add(new CityValidator(this, departureCodeField));
return departureField;
}
private HiddenField departureCodeField() {
departureCodeField = new HiddenField("departureCodeField", model.bind("departureCode"));
departureCodeField.setMarkupId("departureFieldCode");
return departureCodeField;
}
private Component flightType(){
flightChoices = new RadioGroup("flightTypes");
flightChoices.setModel(model.bind("tripType"));
flightChoices.add(listOfRadio(flightsTypeList(), "flightType"));
return flightChoices;
}
private List flightsTypeList() {
return Arrays.asList(
new RadioOptionObject("one way", new Model(TRIP_TYPE_ONE_WAY)),
new RadioOptionObject("return", new Model(TRIP_TYPE_RETURN))
);
}
private Component travellingWhen(){
return new Label("travellingWhen", new StringResourceModel("travelling_when", this, new Model("")).getString());
}
private Component dateType(){
dateChoices = new RadioGroup("dateTypes");
dateChoices.setModel(model.bind("dateType"));
dateChoices.add(listOfRadio(datesTypeList(), "dateType"));
return dateChoices;
}
private List datesTypeList() {
return Arrays.asList(
new RadioOptionObject("Flexible dates", new Model(DATE_TYPE_FLEX)),
new RadioOptionObject("Fixed dates", new Model(DATE_TYPE_FIX)));
}
private ListView listOfRadio(final List flightDateOptionValues, final String componentId) {
ListView listView = new ListView(componentId + "sList", flightDateOptionValues) {
#Override
protected void populateItem(final ListItem listItem) {
final Radio radio = new Radio(componentId + "Radio", ((RadioOptionObject) listItem.getModelObject()).getRadioModel()) {
#Override
public String getValue() {
return listItem.getDefaultModelObjectAsString();
}
#Override
protected boolean getStatelessHint() {
return true;
}
};
radio.add(new AjaxEventBehavior("onchange") {
#Override
protected void onEvent(AjaxRequestTarget target) {
tripSearchModel.setDateType(radio.getModelObject().toString());
refreshPanel(target);
}
});
listItem.add(radio);
listItem.add(new Label(componentId + "Name", new StringResourceModel(radio.getModelObject().toString(), this, radio.getModel())));
}
};
return listView;
}
private void refreshPanel(AjaxRequestTarget target) {
this.remove(optionsContainer);
target.addComponent(optionsContainer(visibility()));
}
private List visibility() {
return visibilityMode(((TripSearchModel) model.getObject()).getDateType());
}
private Component optionsContainer(List visibility){
optionsContainer = new WebMarkupContainer("optionsContainer");
optionsContainer.add(flexibleDates(visibility.get(0)));
optionsContainer.add(fixedDates(visibility.get(1)));
optionsContainer.add(passengersAndClass(visibility.get(2)));
optionsContainer.setOutputMarkupId(true);
optionsContainer.setVisible(true);
return optionsContainer;
}
private Component flexibleDates(Boolean visibility){
flexibleDates = new FlexibleDates("flexibleDates", model);
flexibleDates.setOutputMarkupId(true);
flexibleDates.setVisible(visibility);
return flexibleDates;
}
private Component fixedDates(Boolean visibility){
fixedDates = new FixedDates("fixedDates", model);
fixedDates.setOutputMarkupId(true);
fixedDates.setVisible(visibility);
return fixedDates;
}
private Component passengersAndClass(Boolean visibility){
passengersAndClass = new PassengersAndClass("passengersAndClass", model);
passengersAndClass.setOutputMarkupId(true);
passengersAndClass.setVisible(visibility);
return passengersAndClass;
}
private List visibilityMode(String dateType) {
if(DATE_TYPE_FIX.equalsIgnoreCase(dateType)){
return FIX_CONTAINER_VISIBLE;
} else if(DATE_TYPE_FLEX.equalsIgnoreCase(dateType)){
return FLEX_CONTAINER_VISIBLE;
} else{
return HIDE_CONTAINERS;
}
}
}

I think one potential issue you may have is that you listen for ajax-onchange events and you attempt to make changes to panels depending on the model supposedly having changed. In my experience with radio-type form components, you may need to use AjaxFormComponentUpdatingBehavior (instead of AjaxEventBehavior) in order to capture changes to a model of such form-components. Hope this helps!
Edit: Instead of listing caveats (you need to use another type of behavior for some form components), I'll just add a link to the documentation: Javadoc for AjaxFormComponentUpdatingBehavior

At the end of the day I find out that there was another Easter egg hidden for me.
radio.add(new AjaxEventBehavior("onchange") {
#Override
protected void onEvent(AjaxRequestTarget target) {
tripSearchModel.setDateType(radio.getModelObject().toString());
refreshPanel(target);
}
I was changing same date parameter on event of two different radio groups, which rendered form useless. That was one change, the second change was moving from WebMarkupContainer to EnclosureContainer that was suggested to use on Wicket mailing list for components changing their visibility status. Nevertheless I will give it a try with AjaxFormComponentUpdatingBehavior thank you #Martin Peters

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.

Multi select drop down in Wicket

How to implement multiple select drop down in Wicket. I am able to create multi select drop down view using bootstrap but I am not able to get how to relate selected options with IModel of drop down component? Is there any possibility in Wicket? I do not want to use ListMultipleChoice.
Here is a sample code.
{
private IModel<List<? extends String>> statusChoices;
private DropDownChoice<String> status;
private String statusFilter = "firstChoice";
// List of Items in drop down
statusChoices = new AbstractReadOnlyModel<List<? extends String>>() {
#Override
public List<String> getObject() {
List<String> list = new ArrayList<String>();
list.add("firstChoice");
list.add("secondChoice");
list.add("thirdChoice");
return list;
}
};
status = new DropDownChoice<String>("status",new PropertyModel<String>(this, "statusFilter"), statusChoices);
status.add(new AjaxFormComponentUpdatingBehavior("onchange") {
#Override
protected void onUpdate(AjaxRequestTarget target) {
if(statusFilter.equals("firstChoice"))
// Do Somthing
else
// Do Somthing
}
});
}

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.

GWT CellTable and SimplePager issue

I am using a CellTable<Contact> in my GWT 2.4 project. Everything worked perfectly, so I decided to add paging to the table by using a SimplePager. The CellTable now displays the correct number of entries (page size), but all the pager buttons are disabled.
I am doing the following:
...
#UiField(provided=true) CellTable<Contact> contactsTable = new CellTable<Contact>();
#UiField SimplePager pager;
private TextColumn<Contact> nameColumn;
private TextColumn<Contact> surnameColumn;
public ViewContactsViewImplDesktop() {
initWidget(uiBinder.createAndBindUi(this));
initTable();
}
#Override
public final void updateContactList(final ArrayList<Contact> contacts) {
contactsTable.setRowCount(contacts.size());
final ListDataProvider<Contact> dataProvider = new ListDataProvider<Contact>();
List<Contact> list = dataProvider.getList();
for (final Contact c : contacts) {
list.add(c);
}
dataProvider.addDataDisplay(contactsTable);
pager = new SimplePager();
pager.setDisplay(contactsTable);
pager.setPageSize(3);
ListHandler<Contact> nameColumnSorter = new ListHandler<Contact>(list);
ListHandler<Contact> surnameColumnSorter = new ListHandler<Contact>(list);
nameColumnSorter.setComparator(nameColumn, new Comparator<Contact>() {
#Override
public int compare(Contact c1, Contact c2) {
return c1.getName().compareTo(c2.getName());
}
});
surnameColumnSorter.setComparator(surnameColumn, new Comparator<Contact>() {
#Override
public int compare(Contact c1, Contact c2) {
return c1.getSurname().compareTo(c2.getSurname());
}
});
contactsTable.addColumnSortHandler(nameColumnSorter);
contactsTable.addColumnSortHandler(surnameColumnSorter);
contactsTable.getColumnSortList().push(nameColumn);
}
private void initTable() {
nameColumn = new TextColumn<Contact>() {
#Override
public String getValue(Contact contact) {
return contact.getName();
}
};
surnameColumn = new TextColumn<Contact>() {
#Override
public String getValue(Contact contact) {
return contact.getSurname();
}
};
nameColumn.setSortable(true);
surnameColumn.setSortable(true);
contactsTable.addColumn(nameColumn, "Name");
contactsTable.addColumn(surnameColumn, "Surname");
}
Thanks!
Not setting the page size and/or not setting the table's row count manually could do the trick, as hinted in my comment.
I'd love to provide a concise code sample but do not have access to any code using cell widgets right now.