Search code examples
jsfprimefacesdialogview-scope

p:fileUpload inside p:dialog losing @ViewScoped values


I'm trying to update multiple files with <p:fileUpload>. After I upload the files, I set a List with the paths that works fine.

After that the user then has to fill some other optional information and then click on a button(to submit the form).

When the user clicks on the button all the information about the list that was done on the public void handleFileUpload(FileUploadEvent event) is lost.

I need to save the paths on the database only when the user clicks on the button, i don't understand why the values are being lost, I'm using @javax.faces.view.ViewScoped

Also when the handleFileUpload is being processed the inputs made on the screen by the user are not yet available.

  • JSF 2.2
  • CDI
  • PrimeFaces 5.1

I will omit some parts on the code below to avoid making it huge (if you think there's not enough info just tell me)

XHTML :

<h:form>
    <!-- OMITED -->
    <p:dialog>
        <!-- OMITED -->
        <p:fileUpload fileUploadListener="#{csrBean.handleFileUpload}"
            mode="advanced"
            skinSimple="true"
            cancelLabel="Cancelar"
            multiple="true"
            auto="false"/>
        <!-- OMITED -->
    </p:dialog>
    <!-- OMITED -->
</h:form>

The method:

public void handleFileUpload(FileUploadEvent event) {
    UploadedFile file = event.getFile();
    String normalize = FilenameUtils.normalize("uploads/csr/"
            + csr.getNumero() + "/" + event.getFile().getFileName());
    File destino = new File(normalize);
    try {
        FileUtils.copyInputStreamToFile(file.getInputstream(), destino);
    } catch (IOException e) {
        e.printStackTrace();
    }

    CsrOsAnexo anexo = new CsrOsAnexo();
    anexo.setCaminho(normalize);
    anexo.setOs(csr.getRespostaRecente().getOs());      
    csr.getRespostaRecente().getOs().getAnexoList().add(anexo);


    FacesMessage message = new FacesMessage("Succesful", event.getFile()
            .getFileName() + " is uploaded.");
    FacesContext.getCurrentInstance().addMessage(null, message);
}

Debugging, I can see the csr.getRespostaRecente().getOs().getAnexoList() filled with all the archives paths but as soon as the handleFileUpload() ends and I go to the method called by the commandButton, those values are gone and the values of the form are filled.


Solution

  • A modal dialog must have its own form.

    <h:body>
        ...
        <p:dialog>
            <h:form>
                ...
            </h:form>
        </p:dialog>
    </h:body>
    

    Because, when the modal dialog is generated into HTML output, it's by JavaScript relocated to the end of HTML <body> which causes it to not be sitting in any form anymore. This relocation is necessary to guarantee compatibility with older browsers (read: IE<9) having trouble with modal overlays and z-indexes. The generated HTML DOM tree ends up to look like this (use webbrowser's dev tools to see it):

    <body>
        ...
        <form>
            ...
        </form>
        ...
        <div class="ui-dialog ...">
            ...
        </div>
    </body>
    

    That the file upload apparently still worked without the form is because it autocreates a hidden iframe with therein a form to simulate the "ajax experience". However, any other action would basically lose the JSF view state and any view scoped bean would therefore get recreated.