I have a UI that has options to upload multiple documents of specific natures to an issue. I am able to upload a single file without problems throughout the rest of my application.
Environment
Code explanation
Here is the code to loop through the document information entities. These entities are either records from the database, or placeholders. The entities will either have an ID that points to the item in the database if it exists, otherwise it will be 0, meaning that it is a placeholder and the file can be uploaded.
In the placeholder situation, there is an upload button which brings up a Primefaces dialog that has a Tomahawk file upload component in it.
Code
Here's the JSF code:
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:t="http://myfaces.apache.org/tomahawk"
xmlns:p="http://primefaces.prime.com.tr/ui"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<ui:repeat var="extDoc" value="#{reportBean.externalDocs}"
varStatus="extDocIdx">
<!-- Display the document name -->
<h:outputText value="#{extDoc.name}"/>
<!-- if the document is not in the database, give the option to add it -->
<ui:fragment rendered="#{extDoc.id == 0}">
<!-- On click of the upload button, display the dialog -->
<h:commandButton value="Upload" type="button"
onclick="uploadDlg#{extDocIdx.index}.show()" modal="true"/>
<p:dialog header='Upload document for #{extDoc.name}'
modal="true" widgetVar="uploadDlg#{extDocIdx.index}"
width="650" minWidth="650">
Select the file to upload:
<!-- THIS IS WHERE THE PROBLEM IS -->
<t:inputFileUpload value="#{reportBean.uploadedFile}"/>
<br/>
<h:commandButton value="Submit"
action="#{reportBean.addExtDocument(extDoc.name, extDocIdx.index)}"/>
</p:dialog>
</ui:fragment>
<ui:fragment rendered="#{extDoc.id != 0}">
<!-- display a link to the uploaded file -->
</ui:fragment>
</ui:repeat>
And the uploadedFile property in the ReportBean:
private UploadedFile uploadedFile;
public UploadedFile getUploadedFile() { return uploadedFile; }
public void setUploadedFile(UploadedFile value) { uploadedFile = value; }
public void addExtDocument(String name, int idx)
throws IOException
{
// access uploadedFile to persist the information
}
The Problem
I foolishly have only one uploadedFile variable to deal with an entire loop of uploaded files; therefore the last item in the loop always overwrites the others, making it impossible to upload any but the last item. I obviously need to specify a different uploadedFile for each time through the loop. I've tried unsuccessfully to use a List<UploadedFile>, but it's not clear how to initialize the array or how the t:inputFileUpload component would update the value on submit.
The Question
So the question is: What kind of EL do I include in t:inputFileUpload and what kind of property in ReportBean do I use to have separate instances of uploadedFile available in my addDocument method?
You can use either List<UploadedFile>
or UploadedFile[]
and access the individual items using the brace notation wherein you pass the current index of the <ui:repeat>
as follows:
<t:inputFileUpload value="#{reportBean.uploadedFiles[extDocIdx.index]}"/>
Either way, you need to make sure that the property is properly preinitialized. The List
must be initialized with new ArrayList<>()
and the array must be preinitialized with exactly the right length. JSF/EL namely won't precreate it for you; it merely sets the given item on the given index and that's all. On a null
list or array, you would only face a PropertyNotWritableException
and on an empty array or of the wrong size, you would only face an ArrayIndexOutOfBoundsException
.