This question already has an answer here:
-
How to find out client ID of component for ajax update/render? Cannot find component with expression “foo” referenced from “bar”
4 answers
What are the possible naming containers in PrimeFaces? Why it is necessary to append naming container id for Ajax update call when we want to update some UI control on form using update=":mainForm:MainAccordian:userNameTextbox"
?
After having IntelliJ scan all my JARs for implementations of javax.faces.component.NamingContainer
here is what I found:
From PrimeFaces 5.3
- AccordionPanel
- Carousel
- Columns
- DataGrid
- DataList
- DataScroller
- DataTable
- Page
- Ring
- SubTable
- Subview
- TabView
- TreeTable
- UIData
- UITabPanel
From MyFaces 2.1
- HtmlDataTable
- HtmlForm
- UITree
- UIForm
What are the possible naming container in Prime faces
In JSF naming containers derive from UINamingContainer.
why it is necessary to append naming container id for Ajax update call when we want to update some UI control on form using update=":mainForm:MainAccordian:userNameTextbox"
Lets say, <h:outputText value="test1" id="userNameTextbox" />
and you add another <h:outputText value="test2" id="userNameTextbox" />
to your page, you will get an error which says that you have a duplicate ID. You can look it up here at the JavaDoc for UIComponent.setId(String):
Set the component identifier of this UIComponent (if any). Component identifiers must obey the following syntax restrictions:
Must not be a zero-length String.
First character must be a letter or an underscore ('').
Subsequent characters must be a letter, a digit, an underscore (''), or a dash ('-').
.. furthermore, important for you:
The specified identifier must be unique among all the components (including facets) that are descendents of the nearest ancestor UIComponent that is a NamingContainer, or within the scope of the entire component tree if there is no such ancestor that is a NamingContainer.
Means that you cannot have two components with the same ID under the same NamingContainer (if you have no NamingContainer at all, the entire tree is counted as NamingContainer).
Therefore you need to add a NamingContainer, like a <h:form id="myNamingContainer" />
Lets take following example:
<h:outputText value="test1" id="userNameTextbox" />
<h:form id="container1">
<h:outputText value="test2" id="userNameTextbox" />
</h:form>
<h:form id="container2">
<h:outputText value="test3" id="userNameTextbox" />
</h:form>
.. and you want to do an update to userNameTextbox. Which userNameTextbox are you refering to because there are 3?
The first one? Then update userNameTextbox
The second one? Then update container1:userNameTextbox
The third one? Then update container2:userNameTextbox
Naming Containers in Prime Faces
As we can see in JSF Reference
NamingContainer is an interface that must be implemented by any UIComponent that wants to be a naming container. Naming containers affect the behavior of the UIComponent.findComponent(java.lang.String) and UIComponent.getClientId() methods;
So to find Naming Containers in PF you need to check hierarchy of NamingContainer interface. In Eclipse you can do this for example by Ctrl+T shortcut on NamingContainer.
In PF 5.3 there are for example: AccordionPanel, Carousel, Columns, DataGrid, DataList, DataScroller, DataTable, Ring, SubTable, TabView, Tree, TreeTable.
Naming Container influence on component id
- Default behavior
Naming Container provide a naming scope for its child components. So it always add prefix to his children id. So id of child component is: parent_component_id".concat(":").concat("component_id")
.
There is one pro tip that I had read in JavaServer Faces 2.0, The Complete Reference that even if you not add NamingContainer to your page it is always automatically added by JSF itself :) There also exist special algorith of this creation (Chapter 11: Building Custom UI Component -> Box called "Rules for Creating the Top-Level Component for a Composite Component"). Of course when you don't set id, it will be generate automatically (for example j_idt234). So full component id may look like this: "j_idt123:j_idt234:j_idt345".
- Change component name separator (since JSF 2.x)
There is a way to override default component name separator (":"). You can define it in web.xml as context-param with name javax.faces.SEPARATOR_CHAR. For example:
<context-param>
<param-name>javax.faces.SEPARATOR_CHAR</param-name>
<param-value>-</param-value>
</context-param>
- UIForm attribute "prependId"
To avoid adding scope to child component there is an attribute (only in UIForm component). But this is not recommended solution. Take a look for example at
uiform-with-prependid-false-breaks-fajax-render
Component id usage (for example in "update", "process")
- Whole id
You can use whole id: "componentParent:component". This is not recommended (code will be fragile; any id changes will cause need to change ids in many places).
- Relative ids in same level of naming container
Inside one naming container you can use simple component id.
- PrimeFaces Search Expression Framework
If you don't know this feature please take a look in PrimeFaces documentation. Prime Faces provide Search Expression Framework with couple of very usefull mechanism.
You can search by keywords.
Keywords are the easier way to reference components, they resolve to
ids so that if an id changes, the reference does not need to change.
Core JSF provides a couple of keywords and PrimeFaces provides more
along with composite expression support.
Examples: @this (current component), @form (closest ancestor form), @namingcontainer (closest ancestor naming container), @parent, @widgetVar(name).
You can also mixed those keywords in quite complex paths (Composite Expressions) for example: @form:@parent, @this:@parent:@parent
The second posibility PF gives you are PrimeFaces Selectors (PFS).
PFS integrates jQuery Selector API with JSF component referencing
model so that referencing can be done using jQuery Selector API
instead of core id based JSF model.
So you can for example:
- update all form elements by
update="@(form)"
- update all datatables by
update="@(.ui-datatable)"
- update all components that has styleClass named myStyle by
update="@(.myStyle)"
Quite a powerful tool.