Search code examples
jqueryajaxjsffacelets

JSF dynamic loading almost working - but no commandButtons


I want to dynamically include .xhtml files at runtime, what you might call the JSF front end to an inheritance hierarchy at the entity level. I expect to have a total of 100 x 30-50 line xhtml files that would be loaded only if required to correctly handle the underlying data. A typical use case is that one or two xhtml files would be required to work on the underlying data, so you can imagine that I don't want to load the full 100 every time, it would be criminally stupid to do so.

Now I know that there are endless discussions out there on this topic but after countless hours of research I have got precisely nowhere. I am using Mojarra 2.1.1.

In the example below I initially ui:include an 'empty' file, a file with minimal content (instead of c:catch), then click a commandButton which changes the content to be included to new.xhtml and updates the container of the ui:include with ajax.

The content of the new.xhtml is shown on the page, so this looks good. The problem is that I've got a commandButton in new.xhtml, and when this file is dynamically included the commandButton will not work - the action routine is never called. If you use ui:include new.xhtml (instead of #{bean.page}) it works fine. So a few questions:

  1. Has anyone else got this working?
  2. If not is there another way of doing it?
  3. Why is something so incredibly obvious and useful so hard to do?

I had a play with jQuery load() today, and whilst I can load files using it there are various issues. The files I want to load will contain PrimeFaces dialogs and when loaded by jQuery the dialogs seemed to flash up momentarily on the screen before disappearing, forever, into the abyss. Others report components not having any css styling.

Thanks.

Edit: I should make it clear that my desire to do dynamic load rather than simply navigating to a different page is because I just want to use primefaces dialogs, a different one according to the type of underlying data. It would be a neater way of doing it, if it's possible.

index.xhtml

<?xml version='1.0' encoding='UTF-8' ?>
<!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:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form>
            <h:commandButton value="push" action="#{bean.doit}">
                <f:ajax render="load"/>
            </h:commandButton>
            <br/>
            <h:panelGroup id="load">
                <ui:include src="#{bean.page}"/>
                <ui:remove>
                <ui:include src="new.xhtml"/>
                </ui:remove>
            </h:panelGroup>
        </h:form>
    </h:body>
</html>

new.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:f="http://java.sun.com/jsf/core">
    <h:outputText value="new.xhtml"/>
    <br/>
    <h:commandButton value="inc" action="#{bean.inc}">
        <f:ajax/>
    </h:commandButton>
</html>

empty.xhtml

<html xmlns="http://www.w3.org/1999/xhtml"/>

Bean.java

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

@ManagedBean
@RequestScoped
public class Bean {

    private String page = "/empty.xhtml";

    public String doit() {
        page = "/new.xhtml";
        return null;
    }
    // getters & setters removed
}

Solution

  • The issue turns out to be that ui:include is implemented by a tag handler which is executed during restore view and not re-executed during invoke application, so when it modifies the component tree your application logic is not aware of the changes. This is true only when using partial state saving.

    I copied the code from the ui:include tag handler into a very simple custom component and could at least get action methods to fire, but not reliably.

    The solution is to exclude views requiring this dynamic ui:include functionality from using partial state saving with an entry in web.xml like this:

    <context-param>
      <param-name>javax.faces.FULL_STATE_SAVING_VIEW_IDS</param-name>
      <param-value>/index.xhtml</param-value>
    </context-param>
    

    This has been tested with multiple commandButtons in multiple included page fragments and seems to work reliably (tested with ViewScoped JSF beans). I've also dynamically included a file containing a primefaces dialog and this works ok as well. I hope this is of some use to others as it's taken a good chunk of time out of my schedule, but at least it's working now, which is good.

    Cheers.