Reputation: 8971
So this is not a very general question, but I was hoping some people could give me some pointers on architecture so that I can build the following reusable wicket component.
Here is a rough sketch on skitch:
https://skitch.com/cmagnollay/8sn2s/multitextform
I know, great drawing right? So essentially, this formcomponent (i think this is the right class to use) will be used to add a user defined number of inputs on a form. When the user hits the - button next to a TextInputField it removes that inputField. When they hit the + button, a new blank field is added. Obviously the component will need to use AJAX to update the component when the user clicks the buttons, but my issue is how to structure this. Is this one class? two (one for whole component, one for inputfield with - button), what classes should I be using to do this? I would like the object to be as general as possible to promote reuse. Here is what I have so far:
public class MultiTextInput<T> extends FormComponent<List<T>>
{
private static final long serialVersionUID = 1L;
private final String removeInputButtonName = "removeInputButton";
private final String addInputButtonIdName = "addInputButton";
private int numInputs = 1;
private List<TextField<T>> inputFieldList = new ArrayList<TextField<T>>();
public MultiTextInput(String id, IModel<T> model)
{
super(id);
inputFieldList.add(new TextField<T>("input1", model));
add(inputFieldList.get(0));
addAddInputFieldMarkup();
}
/**
* Adds an "add" button.
*/
private void addAddInputFieldMarkup()
{
Button addInputButton = new Button(this.addInputButtonIdName + numInputs);
addInputButton.add(new AjaxFormComponentUpdatingBehavior("onclick"){
private static final long serialVersionUID = 1L;
@Override
protected void onUpdate(AjaxRequestTarget target)
{
numInputs++;
inputFieldList.add(new TextField<T>("input" + numInputs));
target.add(MultiTextInput.this);
}
});
}
/**
* Adds a "remove" button.
*/
private void addRemoveInputFieldMarkup()
{
Button removeInputButton = new Button(this.removeInputButtonName + numInputs);
removeInputButton.add(new AjaxFormComponentUpdatingBehavior("onclick"){
private static final long serialVersionUID = 1L;
@Override
protected void onUpdate(AjaxRequestTarget arg0)
{
// TODO Auto-generated method stub
}
});
}
}
As I said, I am just trying to get used to thinking about making Wicket components. I have a lot of experience with OO, but just not particularly with wicket. Thanks for any help and direction!
Upvotes: 2
Views: 1747
Reputation: 780
I guess the easiest way to implement the desired behavior would be to use a ListView backed by a List. And just reload after the add/remove button has been pressed.
Here is a code scribble (not tested)
public abstract class MultiTextPanel<T> extends Panel {
public MultiTextPanel(String id, IModel<ArrayList<T>> model) {
super(id, model);
final Form<ArrayList<T>> multiTextForm = new Form<ArrayList<T>>("multiTextForm", model);
add(multiTextForm);
final ListView<T> listView = new ListView<T>("listView", model) {
@Override
protected void populateItem(final ListItem<T> item) {
// TODO Auto-generated method stub
TextField<T> textField = new TextField<T>("textField", item.getModel());
add(textField);
AjaxSubmitLink removeButton = new AjaxSubmitLink("removeButton", multiTextForm) {
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
multiTextForm.getModelObject().remove(item.getModelObject());
target.addComponent(multiTextForm);
}
@Override
protected void onError(AjaxRequestTarget target, Form<?> form) {
//errors should be ignored, we shoudlnt validate in our form, so this shouldnt happen anyway
multiTextForm.getModelObject().remove(item.getModelObject());
target.addComponent(multiTextForm);
}
};
add(removeButton);
}
};
add(listView);
AjaxSubmitLink addButton = new AjaxSubmitLink("addButton", multiTextForm) {
@Override
protected void onError(AjaxRequestTarget target, Form<?> form) {
//errors should be ignored, we shoudlnt validate in our form, so this shouldnt happen anyway
multiTextForm.getModelObject().add(createNewT());
target.addComponent(multiTextForm);
}
@Override
protected void onSubmit(AjaxRequestTarget target, Form form) {
multiTextForm.getModelObject().add(createNewT());
target.addComponent(multiTextForm);
}
};
add(addButton);
}
public abstract T createNewT();}
Basic html:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.sourceforge.net/" xml:lang="en" lang="en">
<wicket:panel>
<form wicket:id="multiTextForm">
<wicket:container wicket:id="listView">
<input type="text" wicket:id="textField" />
<a wicket:id="removeButton">-</a>
</wicket:container>
</form>
<a wicket:id="addButton">+</a>
</wicket:panel>
The only special thing I've done with this is to put a form around the ListView so we are able to just submit inside the Panel we've created (validation is most likely not needed at this stage and should be done in the form that saves the screen).
The downside with this implementation is that you will always reload the complete form and therefore create a lot of overhead. Only 1 row is added/removed but n(-/+)1 are re-rendered.
Upvotes: 1