Search code examples
springjsf-2popuprichfaces

Best approach to call a (not) lazy-loaded rich:popupPanel using independent JSF pages


I am trying to insert a popup which has to be lazy-loaded when clicking a h:commandButton. I insert its content via ui:include. However the preRenderView method is not being fired and the PopupPageModel model property is not being initialized so I get a NullPointerException when trying to invoke the business logic method inside PopupPageController.

The idea is having separate Model/Controller beans for both pages involved but I am not sure this is the correct approach, so I would like to know other approaches or the correct way to implement mine.

Thanks in advance.

Invoking page:

<h:commandButton
value="#{msg['jsf.thisScreen.someText']}">
  <f:param name="theParameter" value="#{InvokingScreenModel.theParameter}" />
  <rich:componentControl target="myPopup" operation="show" />
</h:commandButton>

<rich:popupPanel id="myPopup" modal="true" resizeable="false" autosized="true"
   onmaskclick="#{rich:component('myPopup')}.hide()">
   <f:facet name="header">
      <h:outputText value="#{msg['jsf.anotherScreen.someText']}" />
   </f:facet>
   <f:facet name="controls">
      <h:outputLink value="#" onclick="#{rich:component('myPopup')}.hide(); return false;"></h:outputLink>
   </f:facet>
   <ui:include src="popupPage.xhtml" />
</rich:popupPanel>

Popup page:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:rich="http://richfaces.org/rich"
    xmlns:a4j="http://richfaces.org/a4j"
    xmlns:c="http://java.sun.com/jsf/core">


    <f:metadata>
        <f:viewParam name="theParameter"
                    value="#{PopupPageModel.theParameter}" />
        <f:event type="preRenderView"
                    listener="#{PopupPageController.initialize}" />
    </f:metadata>


    <h:form id="someForm">
       //Some things here
    </h:form>
</ui:composition>

Popup page controller

@Component("PopupPageController")
@Scope("request")
public class PopupPageController {

    @Autowired
    private PopupPageModel model;
    @Autowired
    private SomeService service;

    public void initialize(){       
        if (!FacesContext.getCurrentInstance().isPostback()) {
            //Change some model properties via service methods
        }
    }

    public void doSomething() {

    }

}

Solution

  • I've been struggling with this for several days and I've been unable to find a lazy-loading solution for the popup so I'm posting a solution I managed to do that doesn't include this feature just in case it's useful for someone.

    Invoking page extract

    <h:form>
        <a4j:outputPanel id="stuffDetails">
            //Some ajax-rendered tabs and accordions above the button
            .....
            .....
            
            <a4j:commandButton
                value="Open Popup"
                actionListener="#{PopupController.someInitializationListenerMethod}"
                oncomplete="#{rich:component('thePopup')}.show();"
                render="formPopup">
            </a4j:commandButton>
        </a4j:outputPanel>
    </h:form>
    
    <rich:popupPanel id="thePopup" modal="true"
        resizeable="false" autosized="true"
        onmaskclick="#{rich:component('thePopup')}.hide();">
        <f:facet name="header">
            <h:outputText value="Some header here" />
        </f:facet>
        <f:facet name="controls">
            <h:outputLink value="#"
                onclick="#{rich:component('thePopup')}.hide(); return false;">
            </h:outputLink>
        </f:facet>
        <h:form id="formPopup">
            <ui:include src="popup.xhtml" />
        </h:form>
    </rich:popupPanel>
    

    popup.xhtml

    <?xml version="1.0" encoding="ISO-8859-1" standalone="yes" ?>
    <html>
    
    <ui:composition xmlns="http://www.w3.org/1999/xhtml"
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:rich="http://richfaces.org/rich"
        xmlns:a4j="http://richfaces.org/a4j"
        xmlns:c="http://java.sun.com/jsf/core">
    
        //Some controls shown inside the popup
        ......
        ......
        
        <a4j:commandButton value="Process data and close popup"
            actionListener="#{PopupController.someProcessingMethod}"
            render=":stuffDetails :formPopup"
            oncomplete="#{rich:component('thePopup')}.hide();" />
        <h:commandButton value="Cancel"
            onclick="#{rich:component('thePopup')}.hide(); return false;" />
    
    </ui:composition>
    
    </html>
    

    The render=":stuffDetails :formPopup" in popup.xhtml is to avoid the infamous JSF spec issue 790 described by BalusC here and here.

    Any suggestions welcome.