I am trying to set up a unit test for an Ajax behavior that I have added to a DropDownChoice component. When I make a call to tester.executeAjaxEvent(…) the DropDownChoice model value gets reset to “null”. I am not understanding something fundamental on how ajax works in wicket. Can anybody help me out here? Why is triggering the "change" event, causing the component to set it's model to null.
DropDownPage.html
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head>
<title>DropDownPage</title>
</head>
<body>
<form wicket:id="form">
<select wicket:id="dropDown">
<option>Option 1</option>
</select>
</form>
</body>
</html>
DropDownPage.java
public class DropDownPage extends WebPage {
private static final Logger log = LogManager.getLogger(DropDownPage.class);
public DropDownPage() {
Form<Void> form = new Form<Void>("form");
add(form);
DropDownChoice<String> dropDown = new DropDownChoice<String>("dropDown", new Model<String>(new String()), Arrays.asList(new String[] { "A", "B" }));
dropDown.setOutputMarkupId(true);
dropDown.add(new AjaxFormComponentUpdatingBehavior("change") {
#Override
protected void onUpdate(AjaxRequestTarget target) {
String choice = dropDown.getModelObject();
if (choice == null) {
nullCall(target);
return;
}
switch (choice) {
case "A":
doAStuff(target);
break;
case "B":
doBStuff(target);
break;
default:
unknownType(target);
}
}
});
form.add(dropDown);
}
protected void doAStuff(AjaxRequestTarget target) {
log.info("doAStuff(...)");
}
protected void doBStuff(AjaxRequestTarget target) {
log.info("doBStuff(...)");
}
protected void nullCall(AjaxRequestTarget target) {
log.info("nullCall(...)");
}
protected void unknownType(AjaxRequestTarget target) {
log.info("unknownType(...)");
}
}
TestDropDownAjax.java
public class TestDropDownAjax {
private WicketTester tester;
#Before
public void setup() throws Exception {
tester = new WicketTester();
}
#After
public void tearDown() {
tester.destroy();
}
int aCount = 0;
int nullCount = 0;
#SuppressWarnings("unchecked")
#Test
public void testDropDownPage() {
DropDownPage dropDownPage = new DropDownPage() {
#Override
protected void doAStuff(AjaxRequestTarget target) {
super.doAStuff(target);
++aCount;
}
#Override
protected void nullCall(AjaxRequestTarget target) {
super.nullCall(target);
++nullCount;
}
};
DropDownChoice<String> dropDown = (DropDownChoice<String>) dropDownPage.get("form:dropDown");
assertNotNull(dropDown);
List<String> choices = (List<String>) dropDown.getChoices();
String choice = choices.get(0);
dropDown.setModelObject(choice);
tester.startPage(dropDownPage);
tester.assertModelValue("form:dropDown", dropDown.getModelObject());
assertEquals(choice, dropDown.getModelObject());
assertEquals(0, nullCount);
assertEquals(0, aCount);
tester.executeAjaxEvent("form:dropDown", "change");
assertEquals(0, nullCount); // fails here
assertEquals(1, aCount);
}
}
The AjaxFormComponentUpdatingBehavior, prior to calling your update method, calls the internal method inputChanged. This method tries to turn the latest user input into a new value for the dropdown's model. Since you didn't actually input anything new, this will be interpreted as selecting the empty option, causing the model value to become null.
As such, you need to do the form input through WicketTester as well.
One way to do this, is through FormTester:
FormTester formTester = tester.newFormTester("form");
formTester.select("dropDown", 0);
tester.executeAjaxEvent("form:dropDown", "change");
This will make your test pass.
Related
In one of our applications we use the lazy query container to browse a potentially very large dataset. This works great. However, it is possible to select an arbitrary number of rows when using a multi-select table.
In our case, this can lead to selecting up to 500.000 rows (Vaadin limit) and then crash the VM.
Is there a way to limit the number of selected rows?
Here is a sample that shows the problem:
public class UIImpl extends UI {
private int SIZE = 500000;
#Override
protected void init(VaadinRequest request) {
// add a large table
LazyQueryContainer lqc = new LazyQueryContainer(
new QueryFactory() {
public Query constructQuery(QueryDefinition qd) {
return new Query() {
#Override
public int size() {
return SIZE;
}
#Override
public void saveItems(List<Item> addedItems, List<Item> modifiedItems, List<Item> removedItems) { }
#Override
public List<Item> loadItems(int startIndex, int count) {
List<Item> r = new ArrayList<>(count);
for (int i = startIndex; i<startIndex+count;i++) {
PropertysetItem item = new PropertysetItem();
item.addItemProperty("name", new ObjectProperty(i));
r.add(item);
}
return r;
}
#Override
public boolean deleteAllItems() {
return false;
}
#Override
public Item constructItem() {
return null;
}
};
}
},
null,
20,
false
);
lqc.addContainerProperty("name", Integer.class, null);
Table table = new Table();
table.setContainerDataSource(lqc);
table.setMultiSelect(true);
table.setSelectable(true);
table.setImmediate(true);
table.setVisibleColumns("name");
table.setSizeFull();
table.addValueChangeListener(new Property.ValueChangeListener() {
public void valueChange(Property.ValueChangeEvent event) {
System.err.println(event.getProperty().getValue());
}
});
setContent(table);
}
}
If you want to limit the number of rows a user is able to select you can use something similar to the following code:
public class TableWithSelectionLimit extends Table {
private final int maxSelections= -1;
private String[] lastSelected;
public TableWithSelectionLimit(int maxSelections) {
this.maxSelections = maxSelections;
}
#Override
public void changeVariables(Object source, Map<String, Object> variables) {
String[] selected = (String[]) variables.get("selected");
if (selected != null && selected.length > maxSelections) {
if (lastSelected != null) {
variables.put("selected", lastSelected);
} else {
variables.remove("selected");
}
markAsDirty();
} else {
lastSelected = selected;
}
super.changeVariables(source, variables);
}
}
This is of course optimizable, but it gives you an idea on how you could do it.
Update
For handling also selections produced using "Shift"+Click one has to handle/update these selection ranges additionally inside the method mentioned above.
Those can be retrieved using variables.get("selectedRanges") that will return a String[] containing items like "8-10" whereas the
first number is: the start index of the selection range
second number is: the amount of items selected starting at this index
Using this information it should be possible to update those values as wished and put them back into the variables using variables.put("selectedRanges", updatedRanges).
Attention: do not forget to call markAsDirty() if the values are changed, as otherwise the changes won't be propagated to the client side.
I am use MVC design pattern. In file FmCompress.zul, I have:
<combobox id="cboFmCompress" model="${$composer.listTypeOfProcess}" mold="rounded" hflex="1">
<attribute name="onCreate">self.setSelectedIndex(1);</attribute>
<template name="model">
<comboitem label="${each.typeOfCompress}" value="${each.typeOfCompressId}"></comboitem>
</template>
</combobox>
Model for combo box: TypeOfCompressDTO.java
public class TypeOfCompressDTO {
private String typeOfCompressId;
private String typeOfCompress;
public TypeOfCompressDTO() {
}
public TypeOfCompressDTO(String typeOfCompressId, String typeOfCompress) {
this.typeOfCompressId = typeOfCompressId;
this.typeOfCompress = typeOfCompress;
}
public String getTypeOfCompressId() {
return typeOfCompressId;
}
public void setTypeOfCompressId(String typeOfCompressId) {
this.typeOfCompressId = typeOfCompressId;
}
public String getTypeOfCompress() {
return typeOfCompress;
}
public void setTypeOfCompress(String typeOfCompress) {
this.typeOfCompress = typeOfCompress;
}
}
In file controller: FmCompressComposer.java , I try something like this (my idea):
public class FmCompressComposer extends BaseCustomComposer<FmCompressService, FmCompressDTO> {
//....
#Wire
private Combobox cboToggleZipUnzip;
//....
// initialize value for combo box.
public ListModel<TypeOfCompressDTO> getListTypeOfProcess() {
lstTypeOfCompress = new ArrayList<TypeOfCompressDTO>();
TypeOfCompressDTO t1 = new TypeOfCompressDTO("1", "Zip file");
TypeOfCompressDTO t2 = new TypeOfCompressDTO("2", "Unzip file");
lstTypeOfCompress.add(t1);
lstTypeOfCompress.add(t2);
listTypeOfProcess = new ListModelList(lstTypeOfCompress, true);
return listTypeOfProcess;
}
// Listen even select item in combo box.
public void onSelect$cboZipUnzip(){
searchDTO = new FmCompressDTO();
searchDTO.setType("1");
// my problem focus at this method, and at this line, get value what user choosen. searchDTO.setType(cboToggleZipUnzip.getSelectedItem().getValue().toString());
List<FmCompressDTO> listDTO = fmCompressService.search(searchDTO);
if (listDTO != null && !listDTO.isEmpty()) {
ListModelList model = new ListModelList(listDTO);
model.setMultiple(true);
gridDataFmCompress.setModel(model);
refreshGridData(null);
}
}
//...
}
Please help me: In combo box, when user has event selecting, call method. (In method, get value what user choosen from combobox).
I assume your BaseCustomComposer is extending a GenericForwardComposer.
If so, you are strictly bound to the naming conventions.
As your id of the combobox is cboFmCompress your wired variable should be
// no need for #Wire
private Combobox cboFmCompress;
and the event listener method should be
public void onSelect$cboFmCompress(Event event) {}
Here you can find a minimized zkfiddle: http://zkfiddle.org/sample/3hnhc92/2-SO-33120026
I'm developing a Vaadin application. Now, I have a trouble with a BeanItemContainer. I have a few items inside my container.
private void populateTable() {
tableContainer.removeAllItems();
for(MyBean myBean : beans){
tableContainer.addItem(myBean);
}
}
When I select the item in the table, I bind the item selected with the binder and I fill the form automatically
table.addItemClickListener(new ItemClickListener() {
public void itemClick(ItemClickEvent event) {
myBean = ((BeanItem<MyBean>) event.getItem()).getBean();
//BeanFieldGroup<MyBean>
binder.setItemDataSource(myBean);
}
});
private Component makeForm() {
formLayout = new FormLayout();
binder.bind(comboBoxModPag,"modPagamento");
binder.bind(fieldInizioVal, "id.dInizioVal");
formLayout.addComponent(comboBoxModPag);
formLayout.addComponent(fieldInizioVal);
formLayout.addComponent(binder.buildAndBind(getI18NMessage("dValidoAl"), "dValidoAl", DateField.class));
return formLayout;
}
Now, I have to manage the user interactions in a different way. For example, if the user modify the value inside the combobox, I have to add a new Bean in the container, while if the users modify the value of the field fieldInizioVal I have to update the current Bean.
insertOrUpdateButton.addClickListener(new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
tableContainer.addItem(myBean));
}
});
But, when add a new Item, the container adds the new item correctly but modify also the old item selected.
How can I do?
I solved in this way
comboBoxModPag.addValueChangeListener(new ValueChangeListener() {
public void valueChange(ValueChangeEvent event) {
MyBean oldValue = (MyBean) comboBoxModPag.getOldValue();
MyBean newValue = (MyBean) comboBoxModPag.getValue();
if( oldValue!=null && newValue!=null && !oldValue.equals(newValue) ){
insertMode = true;
}
else{
insertMode = false;
}
}
}
});
protected void saveOrUpdateModPagContrattoSito() {
if(insertMode){
MyBean newMyBean = new MyBean(myBean);
//Do somethings to restore myBean statuse
//....
//....
tableContainer.addBean(newMyBean);
}
else{
tableContainer.addBean(myBean);
}
table.refreshRowCache();
}
But I don't know if this is the correct way.
I'm having trouble to adding input validation for a double in wicket when useing an ajaxEditableLabel.
This is my code: (item is a listitem from a listview)
item.add(new AjaxEditableLabel("myDouble", new Model(myObject.getMyDouble())) {
#Override
protected void onSubmit(AjaxRequestTarget target) {
super.onSubmit(target);
myObject.setMyDouble(new Double(getEditor().getInput())); //here it fails to read the input when a use enters a wrong number
//Do something when it's a double
}
});
How can I add a validator to this component to check wheter this a double value?
At the moment I'm using:
Double.parseDouble(myval);
With try catch...
But this also needs the input string to be changed because of , and .
There should be a wicket way to validate this input?
Edit**:
Maybe I have to add NumericTextField to this component but I don't understand how.
Check this:
Java:
public class MyPage extends WebPage {
private List<Double> list = Arrays.asList(2013.0, 100.500);
public MyPage() {
final FeedbackPanel feedback = new FeedbackPanel("feedback");
feedback.setOutputMarkupId(true);
add(feedback);
ListView<Double> items = new ListView<Double>("items", new PropertyModel(this, "list")) {
#Override
protected void populateItem(ListItem<Double> item) {
item.add(new AjaxEditableLabel("item", item.getModel()) {
#Override
protected void onSubmit(AjaxRequestTarget target) {
System.out.println(Arrays.toString(list.toArray()));
target.add(feedback);
super.onSubmit(target);
}
#Override
protected void onError(AjaxRequestTarget target) {
target.add(feedback);
super.onError(target);
}
}.add(new IValidator<Object>() {
#Override
public void validate(IValidatable<Object> validatable) {
String in = String.valueOf(validatable.getValue());
try {
Double.parseDouble(in.replace(".", ","));
} catch (Exception ignore) {
try{
Double.parseDouble(in.replace(",", "."));
}catch (Exception e){
ValidationError error = new ValidationError(String.format("`%s` is not a Double", in));
validatable.error(error);
}
}
}
}));
}
};
add(items);
}
}
Markup:
<div wicket:id="feedback"/>
<ul wicket:id="items">
<li wicket:id="item"></li>
</ul>
The validator above made just for demonstration, in real code I suggest to create a separate class (not an anonimous class).
I solved by changing getInput to getConvertedInput(); This was my old way of solving it.
Using getModelObject() is better and since the Type is set to Double this is better.
I've also changed the type to Double. .setType(Double.class
item.add(new AjaxEditableLabel("myDouble", new Model(myObject.getMyDouble())) {
#Override
protected void onSubmit(AjaxRequestTarget target) {
super.onSubmit(target);
myObject.setMyDouble((Double)getEditor().getModelObject())); //changes here!!!
//Do something when it's a double
}
}.setType(Double.class));
I'm bit confused. I have the following:
public static String showInputDialog() {
Form frm = new Form();
final Command cmd = new Command("Ok");
final TextField txt = new TextField("Enter the text", null, 1024, 0);
frm.addCommand(cmd);
frm.append(txt);
frm.setCommandListener(new CommandListener() {
public void commandAction(Command c, Displayable d) {
if (c == cmd) {
return txt.getString(); // Error !!
} else {
return null; // Error !!
}
}
});
}
As you can see, I want to return the input dialog string, while the anonymous class method should return void. How can I resolve this problem?
This does not work as you expected.
I see there are already some solutions, but I feel a bit more discussion about what is actually going on might be helpful.
When you call the frm.setCommandListener(new CommandListener() { ... }) the code presents the user with a dialog where she can type in some text and submit, but the code does not stop and wait until the user finishes.
Instead the code continues to execute - without yielding the result. Only after the user finished typing and submits, you get called back to process the result - which might happen much later, or not at all.
I guess you have some code calling this method like:
public void someMethod(int foo, String bar) {
[...]
String result = MyInputForm.showInputDialog();
// do something with the result
System.out.println("hey, got a result "+ result);
[...]
}
Instead you need to reorganize this. First write a helper class handling the result:
public static class MyCallBack {
public MyCallBack(... /* here pass in what you need to process the result*/) {
... remember necessary stuff in instance variables
}
public void processResult(String result) {
// do something with the result
System.out.println("hey, got a result "+ result);
[...]
}
}
then the calling side does just:
public void someMethod(int foo, String bar) {
[...]
MyInputForm.showInputDialog( new MyCallBack(... here pass in stuff ...) );
[...]
}
and the actual code has to be changed to:
public static String showInputDialog(final MyCallBack callback) {
Form frm = new Form();
final Command cmd = new Command("Ok");
final TextField txt = new TextField("Enter the text", null, 1024, 0);
frm.addCommand(cmd);
frm.append(txt);
frm.setCommandListener(new CommandListener() {
public void commandAction(Command c, Displayable d) {
if (c == cmd) {
return callback.processResult(txt.getString());
} else {
return; // or just omit the else part
}
}
});
}
Two issues:
this way of programming feels pretty backwards, but it is really the way it works.
what feels not right is that I need to define a second helper class aside of the CommandListener. That is really not good style. I hope it can be improved, but as I do not see the complete code (which would be too much information anyway), I have to leave it to you to improve the code and get rid of the clutter. While I feel you want to have a modular, reusable input dialog helper, this might not be the best approach; better define the Form,TextField and Command directly where you need the result and get that running. Make it reusable in a second step after you get it running.
You don't need to return it if you instead do something with the String or store it somewhere, for example:
static String result;
public String commandAction(Command c, Displayable d) {
if (c == cmd) {
result = txt.getString();
} else {
result = null;
}
}
Although you'll have threading issues to deal with.
Given that CommandListener is fixed, 2 possible options are
Use a class member variable in the outer class & assign to that variable instead
private static String myText;
...
public static String showInputDialog() {
...
frm.setCommandListener(new CommandListener() {
public void commandAction(Command c, Displayable d) {
if (c == cmd) {
myText = txt.getString();
} else {
myText = null;
}
}
});
}
or Create a concrete implementation of your CommandListener and set the return value as a property of the new implementation
I would have a look at making the method/variable in this snippet non-static...
You cant return the string because you dont know when the listener will be called.
You can do something with it once you have the string though.
public static void showInputDialog() {
StringHandler sh = new StringHandler();
frm.setCommandListener(new CommandListener() {
public void commandAction(Command c, Displayable d) {
if (c == cmd) {
sh.handle(txt.getString());
} else {
sh.handle(null);
}
}
});}
public class StringHandler {
public void handle(String s){
// Do something with that string.
}
}