Render hidden elements using JSF and AJAX

1.4k Views Asked by At

I've been facing some problems using JSF with AJAX to render a table without reloading the whole page every time I submit a form.

When I first run the server, my database is empty, so the page is supposed to show only a form to add books. When user submits the form, a fieldset whith all books is supposed to be rendered. I don't want this fieldset to be shown when database is empty.

This is a simple version of my code (it is just a small form and a table to be refreshed using AJAX):

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">

<h:head />
<h:body>
<h:graphicImage library="img" name="caelum-logo.png"/>

<h:form>
    <p>Book Title:</p>
    <h:inputText id="title" value="#{livroBean.livro.titulo}" />
    <h:commandButton value="Add book" action="#{livroBean.addFirstBook}">
        <f:ajax execute="@form" render="title :addedBooksTable" />
    </h:commandButton>
</h:form>

<div id="addedBooksTable">
    <p:fieldset rendered="#{livroBean.numberOfBooks > 0}">
        <h:dataTable value="#{livroBean.allBooks}" var="book">
            <h:column>
                <h:outputText value="#{book.titulo}" />
            </h:column>
        </h:dataTable>
    </p:fieldset>
</div>

</h:body>
</html>

And i wanna focus on this part:

<h:commandButton value="Add book" action="#{livroBean.addFirstBook}">
    <f:ajax execute="@form" render="title :addedBooksTable" />
</h:commandButton>

The fieldset and the table inside it are supposed to be hidden when there's no book added to the database, that's why i used <p:fieldset rendered="#{livroBean.numberOfBooks > 0}">. But I want it to be rendered after I click the commandButton, even if there's nothing at the inputText.

Here's what's happening when I test the code:

  • if I test the code just as it is with an empty database, the inputText is refreshed (it "erases" what were typed before the submisssion) when I click on the commandButton, but the fieldset is not. I know that the fieldset has a rendered="#{livroBean.numberOfBooks > 0}" and the inputText does not, but the method getNumberOfBooks is called everytime i click the commandButton, that's why I don't get it...
  • if I change the f:ajax tag so it ends up like this <f:ajax execute="@form" onevent="click" render="title :addedBooksTable" />, it solves the problem, but i can realize the screen flashing for a while when I click the commandButton. As far as I know, one of the uses of AJAX is that we don't want the screen flashing when a request is made.

Why is the fieldset rendered only when I use onevent="click"? Should I consider the flashing something normal? Is there a more elegant solution for that?

Thanks!

1

There are 1 best solutions below

0
On BEST ANSWER

You can't ajax-update a plain HTML element. You can only ajax-update a JSF component. Simple reason is that the target must be resolveable by UIViewRoot#findComponent(), so that JSF can find it in the component tree and render the updated HTML into the Ajax response.

Replace

<div id="addedBooksTable">

by

<h:panelGroup layout="block" id="addedBooksTable">

Normally, this should have thrown an exception as described in How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar", but they removed this check in Mojarra 2.2.5 in order to support ajax-updating a specific iteration of <ui:repeat> and <h:dataTable> (this missing check will be fixed later on as that's indeed unhelpful to starters like you).

As to why adding onevent="click" appear to work, that's because it caused a JavaScript error which in turn caused the whole JavaScript/Ajax thing to break down, which in turn caused that the command button fall backs to default synchronous behavior with a full page reload (as if you aren't using <f:ajax> at all). You likely meant to use event="click" instead. The onevent attribute serves a different purpose. See also a.o. Proccess onclick function after ajax call <f:ajax>.