Multi select drop down in Wicket - 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
}
});
}

Related

Apache Wicket TextField

Good day!
I create some Table:
List<IColumn<User, String>> columns = new ArrayList<>();
columns.add(new AbstractColumn<User, String>(new Model<String>("")) {
#Override
public void populateItem(Item<ICellPopulator<User>> cellItem, String componentId, IModel<User> rowModel) {
cellItem.add(new Link<String>(componentId) {
#Override
public void onClick() {
System.out.println("editors" + rowModel.getObject().getName());
PageParameters parameters = new PageParameters();
parameters.add("id", rowModel.getObject().getId());
add(new EditPanel("panel", rowModel));
}
#Override
public IMarkupFragment getMarkup() {
return Markup.of("<div wicket:id='cell'> edit </div>");
}
});
}
When I click on cell into Table, cell markup "Edit", I create some Panel:
public class EditPanel extends Panel {
public EditPanel(String id, IModel<User> model) {
super(id, model);
User user = model.getObject();
if (user == null) {
user = new User();
}
List<UserRole> list = Arrays.asList(UserRole.values());
Form<?> form = new Form("form", new CompoundPropertyModel(user));
TextField<String> userName = new TextField<String>("name");
};
add(new FeedbackPanel("feedback"));
add(form);
form.add(userName);
}
}
How can I set value to
TextField userName = new TextField("name");
from my model, or if model == null, set any text what I need?
Thanks!
If you use Wicket 7 or older then you may use :
TextField<String> userName = new TextField<String>("name", new PropertyModel(model, "username"));
assuming that username is a property of User.
With Wicket 8.x you can use LambdaModel instead.
Heyy,
I think the problem is because you are passing the object and not the model.
The right way is passing with CompoundPropertyModel, like you do. It`s better then PropertyModel in this case.
Try doing this:
...
if (model.getObject() == null) {
model.setObject(new User());
}
Form<?> form = new Form("form", new CompoundPropertyModel(model));
...
Hope help you.

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.

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.

Wicket children panels refresh

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