there are already a lot of questions concerning the usage of:
EL in attributes (How to use EL with <ui:repeat var> in id attribute of a JSF component)
JSTL in Facelets (JSTL in JSF2 Facelets... makes sense?)
view build time (What's the view build time?)
My question should address the general usage of an EL expression in the id attribute of a JSF component.
Assume we have the following example code (the usefulness of this code should not be questioned):
<h:form id="#{bean.property}">
<h:outputText value="#{bean.property}" />
</h:form>
Now I have the following questions:
Is it okay to use an EL expression in the id attribute of h:form or other JSF components?
What are the things to keep in mind if one uses EL expressions in the id attribute?
The id attribute of a JSF component has to be available at view build time. A deferred EL expressions, like above, is evaluated when the attribute is accessed. Consequently, if you have an EL expression in id attribute the result of the EL expression is available at view build time?
I read that an EL expression can be used in attributes of type javax.el.ValueExpression, but the id is often of type String (https://coderanch.com/t/550270/java/null-variable-id-attribute). Consequently you can only use EL expressions in id attribute, if the expression evaluates to a String?
If you google for "EL expression in id attribute", there are people stating you cannot use EL expressions in id attribute, but the above example code works totally fine. This question is mainly intended for a discussion about the recommendability of the usage of EL expressions in id attribute.
Thanks for your answers!
No, never do it, id should not be dynamic . QA team will reject it.
Related
According to https://code.google.com/p/primefaces/issues/detail?id=4720, The ComponentUtils.resolveWidgetVar(String expression, UIComponent component) function is available in Primefaces since 2013. It can be used in EL by the "#{p:widgetVarFromContext(searchExpression, component)}" function.
This is useful in case of several components have the same id in different NamingContainer, but are still present in the same view. In this case,
the #{p:widgetVar(searchExpression)} function only returns the last one found.
I don't understand however how to reference the UIComponent that must be passed as the second argument from EL. The above mentioned bug report suggests we can refer to it using #{component}. Can anyone provide me with an example?
The #{component} is an implicit EL variable referring the current UIComponent in EL scope (see also implicit EL objects). You can usually only refer it in component's HTML attribute or in template text children.
E.g. in case of <h:inputText> it will reference an instance of UIInput class which has among others an isValid() method.
<h:inputText id="foo" required="true"
style="background: #{component.valid ? '' : 'pink'}"
onclick="alert('Client ID of this component is #{component.clientId}');" />
You can also use binding attribute to let JSF during view build time put a reference to a component instance in the Facelet scope. This way the component reference will be available anywhere in the Facelet during view render time.
<script>alert('Client ID of foo component is #{foo.clientId}');</script>
<h:inputText binding="#{foo}" />
See also:
Difference between client id generated by component.clientId and p:component()
JSF component binding without bean property
How does the 'binding' attribute work in JSF? When and how should it be used?
The p:widgetVarFromContext is useful when referring to a PrimeFaces widget inside a composite component. There could be more than one instance of your component on the same page. So writing widgetVar="expression" and PF('expression') is out of the question. There would be multiple widgets with the same name. It is then better to omit the widgetVar attribute and use the generated one which is unique because it is based on the clientId.
You can't use #{p:widgetVar('expression')} within your <cc:implementation> because it leads to a Cannot find component for expression "expression" referenced from "j_id1" error instead of the expected PF('widget_expression').
But you can use #{p:widgetVarFromContext('expression', cc)} which will return something like PF('widget_wrapperform_compositecomponent1_expression'). The cc refers to the root of the composite component instance.
I would like to display an internationalized message in the place holder below. All examples I'm hitting, work with a message bundle defined in faces-config and simply use that bundle name. But that works only when we know what message key to use. This isn't the case here.
Consider this basic example:
<h:dataTable border="1" value="#{indexBean.webAdminCatalogList}" var="menuItem">
<h:column>#{message key here}</h:column>
</h:dataTable>
The code above loops through a list. Each menuItem variable contains a titleMessageCode which corresponds to a field in the bundle properties files. How do you go about fetching a message using menuItem.titleMessageCode? Is there a special JSF tag or are such internationalized labels prepared when constructing the bean?
You can use the brace notation #{map[dynamicKey]} to use a dynamic variable as map key. Given a bundle name of text, here's an example:
<h:dataTable border="1" value="#{indexBean.webAdminCatalogList}" var="menuItem">
<h:column>#{text[menuItem.titleMessageCode]}</h:column>
</h:dataTable>
Note that this is not specific to JSF, but to EL. Those #{} things are not part of JSF, they are part of EL (Expression Language). JSF just happens to make use of it. See also our EL wiki page.
Is this legal?
<h:form id="status${a.myID}" >
// ...
</h:form>
where 'a' is an object in a backing bean. It seems to sort of work, but when I look at the rendered HTML, I see the id as: :0:status for example, instead of :status0 as I would expect. My main problem is trying to reference the id from <f:ajax render=.... I'm getting "contains an unknown id..." with pretty much every combination I can think of. Is it possible to set ids using values from a backing bean reliably?
The single-letter variable name ${a} and the symptom of an iteration index like :0 being auto-appended in client ID, suggests that this <h:form> is inside a JSF iterating component such as <h:dataTable> or <ui:repeat> with a var="a" which actually is not a backing bean. It would confirm and explain all symptoms described so far. If ${a} were a real backing bean (and not an iteration variable), then it would have "worked" and you would have seen :0:status0, :1:status0, :2:status0, etc — whose usefulness is questionable though.
First of all, the id attribute of a JSF component is evaluated and set during view build time, the moment when the JSF component tree is to be built based on XHTML source code file. The var attribue of a JSF iterating component is set during view render time, the moment when the HTML output is to be generated based on JSF component tree. Thus, logical consequence is, the object set by var attribute is not available at the moment the id attribute needs to be set and thus evaluates to null/empty-string. The effect is exactly the same as when you would do
<h:form id="status">
JSF iterating components namely already auto-appends the iteration index to the generated client ID. It would not make any sense to manually set the ID of the iterated item in the component ID. There's namely physically only one <h:form> component in the JSF component tree which is in turn reused multiple times during producing the HTML output based on the current iteration round.
This Q&A should also give more food for thought: JSTL in JSF2 Facelets... makes sense?
Coming back to your concrete functional requirement of referencing a component in <f:ajax render>, you definitely need to solve this differently. Unfortunately you didn't clearly describe the context of the source component and the target component, so it's impossible to give/explain the right client ID and so I'm just giving you a link so that you can figure it out on your own: How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"
Unrelated to the concrete problem, the old JSP EL style ${...} has in Facelets exactly the same effect as #{...}. In order to avoid confusion by yourself and your future maintainers it's recommend to completely ban usage of ${...} and stick to #{...} all the time. See also Difference between JSP EL, JSF EL and Unified EL
Actually ${a.myID} this is not rendering any output. As you are getting :0:status as form ID which implies, :0 is parent of :status in HTML tree structure.
This question already has answers here:
How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"
(6 answers)
Closed 6 years ago.
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"?
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
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
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.
Is EL #{myBean.property} evaluated in example below? If yes, that means all ELs on page are evaluated regardless of value of their 'rendered' (or any other) attribute?
<h:panelGroup rendered="false">
<h:outputText value="#{myBean.property}" />
</h:panelGroup>
Is EL #{myBean.property} evaluated in example below?
No. You could also easily answer it yourself by putting a debug breakpoint on the getter method.
If yes, that means all ELs on page are evaluated regardless of value of their 'rendered' (or any other) attribute?
That basically depends on how well designed the component is. The standard JSF components doesn't do that, but if it's for example a custom component which doesn't check isRendered() inside processXxx methods before continuing processing itself and children, then all the EL of the children may be evaluated.