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 use RepeatingView

    with FileUploadField

    in each element along with the other two fields that I need.

  • The problem is that whenever the "add file" ( AjaxLink

    ) button is clicked FileUploadFields

    , 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 show FileUploadField

    a TextField

    to describe the file and DropDownChoice

    to select the document type (our own classification).
+3


source to share


1 answer


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.

+2


source







All Articles