JSF f:ajax – How to locate componets outside the current context

Using JSF 2.0 and Ajax simplifies the web design and provides a lot of cool tricks to extend the user experience. With the render and execute tags form the f:ajax tag it is use to update components dynamically.

But in the past I run often into problems when trying to update a component outside the current ui context. For example when you try to update a part of your form for a single row in h:datatable component you will typically see log messages like this one:

f:ajax contains an unknown id 'j_idt90:painelTabela' - cannot locate it in the context of the component j_idt75

To solve such kind of problems you need to specify the full component id to be rendered or executed. This is not always as easy at it sounds. When your component is nested in a complex component tree the component id can be prafixed with a long list of parent component ids. This happens typically in h:datatables, ui:repeats or c:forEach sections.

One solution is to navigate back in the tree to the correct ui node and get the componentID using an EL syntax like this:

<f:ajax render=":#{component.parent.parent.parent.parent.clientId}:new-minute-panel:" />

This looks not really nice and it creates new problems when you redesign or move your ui components into a new structure on your jsf page.

A simple trick to solve the problem is to bind the component which need to be rendered into a ui param. So you can reference its full clientId in the ajax context. See the following example:

<h:panelGroup layout="block" id="mylist" binding="#{myListComponent}">                
   <ui:repeat var="attachment" value="#{myController.myList}">
       ....
       <!-- some ajax functionality... -->
       <f:ajax render=":#{myListComponent.clientId}">
           <h:commandLink actionListener="#{myController.soSomething}">click me</h:commandLink>
       </f:ajax>
 ...
   </ui:repeat>
....

In this example I use the binding tag to bind the panelGroup to a param named ‘myListComponent’. Inside the ui:repeat I can now access the outer component and render the component fom my f:ajax element accessing the clientId

<f:ajax render=":#{myListComponent.clientId}"

As a result the ajax component no longer depends on the position in my page and I can reuse it in any part of my jsf page.

TRANSFER CLIENTIDS TO SUBFORMS

Another solution using the binding is to transfer a component into a subform. See the following example:

 <h:panelGroup id="my_panel" binding="#{myListComponent}">
     <h:dataTable id="mydatatable" value="#{myController.myList}" var="child">
       .....
        <ui:include src="/forms/sub_myeditor.xhtml" >
            <ui:param name="myPanel" value="#{myListComponent}" />            
        </ui:include>
       .....
     </h:dataTable>

In this example I transfer the ui:component containing my h:datatable to a subpage ‘sub_myeditor.xhtml’  which is included inside the table. Now I can access the panelGroup inside my subpage and render it after an ajax event:

<h:commandButton ....  >
       <f:ajax render=":#{myListComponent.clientId}" 
                execute=":#{myListComponent.clientId}"/>
</h:commandButton>

Also in this example the component in the ajax tag is addressed with its absolute path.

Leave a Reply

Your email address will not be published. Required fields are marked *