In a single page JSF 2.1 app using RF 4 will be having a lot of popups for different forms to edit data. The forms should only be displayed when a certain button in the GUI is clicked. Since I cannot afford to create all popups including content and backing beans on the original page load I created them with a delayed load similar to the second answer presented in this question: Richfaces modalPanel load with Ajax
<a4j:outputPanel id="container">
<c:if test="#{popupController.isPopupVisible(popup.id)}">
<f:subview id="view">
<rich:popupPanel id="popup" show="true" modal="false" autosized="true" resizeable="false" style="z-index:10000; background-color:#FFF;">
<f:facet name="header">
<h:outputText value="#{popup.title}" />
</f:facet>
<f:facet name="controls">
<h:form id="closeButtonform">
<a4j:commandLink id="closePopup" render="container" action="#{popupController.hidePopup(popup.id)}" onbeforedomupdate="#{rich:component('popup')}.hide(); return false;">X</a4j:commandLink>
</h:form>
</f:facet>
<rich:extendedDataTable id="table" value="#{itemBean.items}" var="item">
...
</rich:extendedDataTable>
</rich:popupPanel>
</f:subview>
</c:if>
</a4j:outputPanel>
<h:form id="openButtonForm">
<a4j:commandLink id="openPopup" render="container" action="#{popupController.showPopup(popup.id)}" />
</h:form>
The backing beans do not do any magic and just store data so they are left out here.
Working so far is open and close of popup links that show/hide the popup and the contents including backing beans for the popup content are only created when needed. The problem I experience is that the components inside the popup (the rich:extendedDataTable in this example) are displayed but they are not correctly processed by JSF/Richfaces/Ajax in some way. Let me elaborate this further: When the c:if test evaluates to true during initial display of the page everything is fine as it should be and the JavaScript RichFaces.$('popup:view:table')
yields the correct RF JavaScript object associated with the table. I can close/open the popup as often as I want without any problem. However when the popup is initially not present on page load and then opened using the button I get the following JS errors.
Uncaught TypeError: Cannot call method 'addCSSText' of undefined popup.xhtml:1
(anonymous function) popup.xhtml:1
runScripts jsf.js.xhtml:1
doUpdate jsf.js.xhtml:1
response jsf.js.xhtml:1
richfaces.queue.response richfaces-queue.js.xhtml:413
jsf.ajax.response richfaces-queue.js.xhtml:50
onComplete jsf.js.xhtml:1
AjaxEngine.req.xmlReq.onreadystatechange jsf.js.xhtml:1
Uncaught TypeError: Cannot call method 'addLocale' of undefined popup.xhtml:1
(anonymous function) popup.xhtml:1
runScripts jsf.js.xhtml:1
doUpdate jsf.js.xhtml:1
response jsf.js.xhtml:1
richfaces.queue.response richfaces-queue.js.xhtml:413
jsf.ajax.response richfaces-queue.js.xhtml:50
onComplete jsf.js.xhtml:1
AjaxEngine.req.xmlReq.onreadystatechange jsf.js.xhtml:1
Uncaught TypeError: undefined is not a function popup.xhtml:1
(anonymous function) popup.xhtml:1
runScripts jsf.js.xhtml:1
doUpdate jsf.js.xhtml:1
response jsf.js.xhtml:1
richfaces.queue.response richfaces-queue.js.xhtml:413
jsf.ajax.response richfaces-queue.js.xhtml:50
onComplete jsf.js.xhtml:1
AjaxEngine.req.xmlReq.onreadystatechange
Then the same JavaScript RichFaces.$('popup:view:table')
yields undefined
and the table in the popup does not render correctly. It seems everything that depends on RF JavaScript like column formatting, scrollbars etc. is not initialized, CSS is not applied in some cases. Here it also does not matter how often I close/open the popup, it is always broken.
Any ideas how I can fix this problem either directly in JSF or in JavaScript on the open button's oncomplete for example?
The problem is that when a component is added to the JSF view tree dynamically it does not properly load resource dependencies of the component.
E.g. when rich:extendedDataTable
component appears in the tree, JSF reads it's resource dependencies from ExtendedDataTableRenderer
, for example one of the dependencies is @ResourceDependency(library="org.richfaces", name = "extendedDataTable.js")
, target
definition defaults to head
, so the resource is added as a child to h:head
in the view tree.
In my mind it would be reasonable for JSF to detect such additions and load new resources on ajax request complete (RichFaces 3 ajax engine used to do so, but since JSF2/RichFaces4 JSF took the whole responsibility and implemented own ajax engine). However JSF does not load the resources added to the head.
Possible solutions/workarounds:
.
<c:if test="#{initParam['javax.faces.PROJECT_STAGE'] eq 'Development'}">
<!-- Force pre-loading all the resources (js/css/etc) of the specified components as they may appear in JSF tree dynamically. -->
<rich:extendedDataTable rendered="false"/>
<!-- Other components go here. -->
</c:if>
h:head
. It is just an idea, but it might lead you to something. E.g. if you do render="@all"
- it works much slower but loads everything as needed, maybe there is a way to make JSF to render the head area.