Reputation: 5684
I am building a Wicket WebPage
that dynamically builds a form with multiple different input types. It is possible to add textfileds, checkboxes and dropdownlists. The different inputs are added via a ListView
and different Panel
s for each type. The creation of the form works, but now I do not know how I can access the entered values in the onSubmit()
method.
This the constructor of my page:
public AddJobPage(final PageParameters params) {
super(params);
final Workable w = WorkableManager.getInstance().findWorkableByName(params.get("workableName").toString());
final List<FormElement> formElements = w.getParameterFormDescriptor();
final ListView<FormElement> listView = new ListView<FormElement>("inputs", formElements) {
@Override
protected void populateItem(final ListItem<FormElement> item) {
switch (item.getModelObject().getType()) {
case TEXT:
item.add(new TextFieldPanel("formElement", item.getModel()));
break;
case DROPDOWN:
item.add(new DropDownPanel("formElement", item.getModel()));
break;
case CHECKBOX:
item.add(new CheckBoxPanel("formElement", item.getModel()));
break;
default:
break;
}
}
};
listView.setReuseItems(true);
final Form<Void> parameterForm = new Form<Void>("parameterForm") {
@Override
protected final void onSubmit() {
// What comes here?
}
};
parameterForm.add(listView);
add(parameterForm);
}
The TextFieldPanel
is defined as following:
public class TextFieldPanel extends Panel {
public TextFieldPanel(final String id, final IModel<FormElement> model) {
super(id, model);
final FormElement el = model.getObject();
final Label label = new Label("label", el.getLabel());
final TextField<String> textfield = new TextField<>("textfield", Model.of(""));
label.add(new AttributeModifier("for", el.getParamName()));
textfield.add(new AttributeModifier("name", el.getParamName()));
textfield.add(new AttributeModifier("id", el.getParamName()));
add(label);
add(textfield);
}
}
FormElement
just describes what type of input should be created. So how do I get the submitted values together with their names (from the HTML-attribute)?
In the end I want to get a Map<String, String>
, where the key is the name and the value the entered value.
Upvotes: 1
Views: 4226
Reputation: 876
I believe you are not using the form correctly.
The main point you're missing is that you should use models as much as you can. That's because components are pretty much always expected to work with some kind of data, and that data is usually taken from the model. So for example, a TextField
's model will be the place where the text field stores it's input. For a Label
, it'll be where it gets its display value from.
What you are doing there, is this: you are ignoring the functionality of TextField which automatically handles input and trying to do this yourself. It's duplication of functionality, and that's not good.
What you should be doing there instead, is using models. To begin with, you should use models on your form components and then in onSubmit you could get the models of those components to get their input.
However, that method is not feasible if you are using more than one form component, as you would have to retrieve the value of the model for each of those in onSubmit()
.
This is where we get to my proposed solution.
Main idea: use a model on your form, and pass that model to all of your form components. Then when the form is submitted, wicket automatically handles the input from those form components and updates the model. Since that model is the model of the form, in the onSubmit
you can access all of the data in just one method call:
Object data = this.getModelObject();
Of course, the value doesn't have to be an Object
, but I'm using it as an example.
However, there is also one difficulty in your scenario; that is the dynamic number of form components. It is not actually difficult to achieve, but it depends on your requirements if you want to identify where the value came from. I assume that you don't, since I would imagine it's just some requirement gathering and proceed with that assumption.
There are 2 ways you can handle this from here on: use a ValueMap
model, or use a custom model object with collections, which will be variable size. I don't like ValueMap
much, so I'm going to skip the explanation on that, but once you understand how models work with forms you should be able to work it out.
So the second way. The first step would be to create a model object for your form. I will assume that you will only have one type of dynamic input, because it's the same process for adding others. So let's say on your page you will have a variable number of TextField
input elements.
public class FormModel {
private List<String> textFieldData = new ArrayList<>();
public void setTextFieldData(List<String> textFieldData){
this.textFieldData = textFieldData;
}
public List<String> getTextFieldData(){
return textFieldData;
}
}
Then when you're creating your form, assign a model of this as the model of the form:
final Form<FormModel> parameterForm = new Form<FormModel>("parameterForm", Model.of(new FormModel());
Now for the more complex part. Each TextField
you use will have to write to that collection and receive data from it. My way of doing it would be to assign a unique integer ID which will be its index on the collection to ensure that each of them will not override the others input. Next, we employ wicket's power and we create a PropertyModel
for each text field, which would point to an element in that collection, so something like:
final TextField<String> textfield = new TextField<>("textfield", new PropertyModel(formModel, "textFieldData." + id));
Where ID is the unique ID assigned to this textfield that is also the index on that collection.
Once you create all of your text fields this way, when the form is successfully validated and onSubmit
is called, the model of the form will be updated with the input of the textfields automatically. In which case you can just use getModelObject()
in your form to retrieve the object of FormModel
class where all of the values are written.
I understand this is a lot to take in, and I might actually be wrong in what you're trying to achieve. But I believe learning how the models work will be the best route to take if you plan to continue using wicket; Models are an integral part of Wicket and they are great once you learn to use them. I suggest reading this piece about how models work and hopefully you'll be able to wrap your head around what I'm trying to explain. All the best of luck to you!
EDIT: One more thing; in your code you assign an AttributeModifier
that sets the ID attribute on your TextField
. That generally is not advisable; you should instead use getMarkupID()
and use the default markup ID in other components, rather than assigning your own to the textfield.
Upvotes: 4
Reputation: 1289
You could iterate the form, get the id and the value and set them in a key value map.
form.visitFormComponents(new IVisitor<Component, Void>()
{
public void component(final Component component, final IVisit<Void> iVisit)
{
component.getMarkupId();
component.getDefaultModelObjectAsString();
}
});
Upvotes: 0
Reputation: 1273
your TextFieldPanel should contain a Model or object to write its output to:
public TextFieldPanel(final String id, final IModel<FormElement> model, IModel<String> content) {
...
final TextField<String> textfield = new TextField<String>("textfield", content);
...
}
Now you can add a Model to the TextField to write the contents to.
Upvotes: 0