Gateway: FileUploadField with ListView
-
I have a page that is used to upload multiple files. For each file, the user has to provide a type and description, so I cannot use
MultiFileUploadField
... so I useRepeatingView
withFileUploadField
in each element along with the other two fields that I need. -
The problem is that whenever the "add file" (
AjaxLink
) button is clickedFileUploadFields
, which already has a file, reset is null ...
What can I do?
Here is ListView
(sorry, it was not RepeatingView
, but a ListView
):
IModel<List<EtdDokument>> listModel = getListModel();
ListView<EtdDokument> dokumenteList = new ListView<EtdDokument>("dokumenteList", listModel) {
private static final long serialVersionUID = 1L;
@Override
protected void populateItem(ListItem<EtdDokument> item) {
final boolean showHeaders = ((getList() == null) || getList().size() == 0);
final WebMarkupContainer headRow = new WebMarkupContainer("headRow");
headRow.setVisible(showHeaders);
item.add(headRow);
EtdDokumentRowPanel etdDokumentRow = new EtdDokumentRowPanel("bodyRow", item.getModel());
item.add(etdDokumentRow);
}
};
dokumenteList.setReuseItems(true);
add(dokumenteList);
AjaxLink<Void> addLink = new AjaxLink<Void>("addDokument") {
private static final long serialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget target) {
EtdConfiguration etdConfig = EtdConfigForm.this.getModelObject();
final EtdDokument newValue = new EtdDokument(etdConfig);
tempEtdDokumente.add(newValue);
target.addComponent(EtdConfigForm.this);
}
};
add(addLink);
-
EtdDokumentRowPanel
has nothing interesting, I just showFileUploadField
aTextField
to describe the file andDropDownChoice
to select the document type (our own classification).
source to share
Ok, this is a bit tricky because the html <input type="file">
lost it after any update and the real model object (the selected file) for FileUploadField
is only set when the event occurs post
. Thus, even if we add AjaxEventBehavior
with an event onchange
to our files panel - this model will be null after the user selects the file.
Actually, we only have access to the selected file prior to post
request from js and you can implement a 'save' script to keep the already selected files in some array while the ajax update is complete and then set them back, but it's tiresome.
So, another way to solve this problem is to update proccess only on the newly added component and not touch others.
I will use RepeatingView
because I only need to generate new wicket ids when I click addLink
and not according to the model. Here's the code (read the comments carefully):
/* Method, which init your form */
private void init()
{
/* Container, which will hold all FileUploadFields,
as RepeatingView adds children to it parent object. */
WebMarkupContainer container = new WebMarkupContainer("container");
/* We need DOM id for container component. */
container.setOutputMarkupId(true);
add(container);
final RepeatingView rv = new RepeatingView("dokumenteList");
container.add (rv);
/* We need to add all default model values by ourselfs for RepeatingView: */
for(EtdDokument doc : getListModel().getObject())
{
createDocumentRow(rv, doc);
}
final AjaxLink<String> addLink = new AjaxLink<String>("addDokument") {
@Override
public void onClick(AjaxRequestTarget target) {
final EtdDokument newValue...;
final EtdDokumentRowPanel r = createDocumentRow(rv, newValue);
...
/* This is it. We dynamicly adding created earlier component to markup.
This allows us to update this component via ajax. */
target.prependJavaScript(
"var item=document.createElement('div');" + //creating empty 'div' tag
"item.id='" + r.getMarkupId() + "'; " + // set markup id for this 'div'.
"Wicket.$('" + container.getMarkupId() + "').appendChild(item);" // add this 'div' as container child.
);
/* Added 'div' is still empty, but this update will replace
it, by real component markup.*/
target.add(r);
}
};
add(addLink);
}
/* This method creates new instance of EDRowP (with random id) and adds
it to RepeatingView.
I have dropped the implementation of your headRow, but you can include it
into the EDRowPanel or implement something similar.
*/
private EtdDokumentRowPanel createDocumentRow( RepeatingView rv, EtdDokument doc )
{
EtdDokumentRowPanel row = new EtdDokumentRowPanel(rv.newChildId(), doc);
rv.add(row);
return row;
}
And in the markup:
<form...>
...
<div wicket:id="container">
<div wicket:id="dokumenteList"></div>
</div>
<a href wicket:id="addDokument">Add</a>
....
</form>
It seems like a too cumbersome solution for a small problem, but I think there is no more elegant one (or maybe I'm too sleepy now to see it). And it should work.
source to share