45

I don't understand how PrimeFaces selectors (PFS) work.

<h:outputText value="#{bean.text1}" styleClass="myClass" />
<p:commandButton update="@(.myClass)" />

I can use it. And I think it's a fantastic tool although it doesn't function always for me. The .myClass is client side jQuery selector. How does JSF on server side know what to update?

I can understand how normal JSF ID selectors work.

<h:outputText value="#{bean.text1}" id="textId" />
<p:commandButton update="textId" />

The textId references an ID of component in the component tree as defined in XHTML file in server side. So I can understand how JSF finds the right component.

But if you are using primefaces selectors, the client side jQuery selectors are used. How does JSF know which component has to be updated? Sometimes I have problems with PFS. It doesn't seem to function always for me. Is there something what you should keep in mind if you are using PFS?

Community
  • 1
  • 1
John N
  • 844
  • 4
  • 12
  • 20

1 Answers1

82

You probably already know that PrimeFaces is using jQuery under the covers. PrimeFaces Selectors are based on jQuery. Anything which you specify in @(...) will be used as jQuery selector on the current HTML DOM tree. For any found HTML element, which has an ID, exactly this ID will ultimately be used in the update.

Basically, for a update="@(.myclass)", PrimeFaces will under the covers roughly do this:

var $elements = $(".myclass");
var clientIds = [];

$.each($elements, function(index, element) {
    if (element.id) {
        clientIds.push(":" + element.id);
    }
});

var newUpdate = clientIds.join(" "); // This will be used as `update` instead.

So, in case of e.g.

<h:form id="formId">
    <h:outputText id="output1" styleClass="myclass" ... />
    <h:outputText styleClass="myclass" ... />
    <h:outputText id="output3" styleClass="myclass" ... />
</h:form>

this command button update

<p:commandButton ... update="@(.myclass)" />

will end up with exactly the same effect as

<p:commandButton ... update=":formId:output1 :formId:output3" />

Note that this also works for autogenerated IDs. I.e. the <h:form id> is not mandatory.


Sometimes I have a problems with PFS. Is there something what you are should keep in mind if you are using PFS ?

It can happen that you selected "too much" (e.g. @(form) doesn't select current form, but all forms, exactly like $("form") in jQuery!), or that you actually selected nothing (when the desired HTML DOM element has actually no ID). Investigating element IDs in the HTML DOM tree and the request payload in the HTTP traffic monitor the should give clues.

The desired elements in the HTML DOM tree must have an (autogenerated) ID. The javax.faces.partial.render request parameter in the HTTP traffic monitor must contain the right client IDs. The element's rendered attribute in the JSF component tree must evaluate true during update. Etcetera.

In your particular example, the <h:outputText> won't end up in the generated HTML output with any ID. Assigning it an id should solve your problem with updating it.

So, this example won't work

<h:form>
    <h:outputText value="#{bean.text1}" styleClass="myClass" />
    <p:commandButton value="Update" update="@(.myClass)" /> 
</h:form>

but this example will work (note that assigning the form an ID is not necessary):

<h:form>
    <h:outputText id="myText" value="#{bean.text1}" styleClass="myClass" />
    <p:commandButton value="Update" update="@(.myClass)" /> 
</h:form>
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 3
    Thank you! Nice explanation. Now I know what my problem was. I have not always used `id` attribute on non-`NamingContainer` components. – John N Nov 19 '13 at 20:55
  • 3
    So to be more clear, components that are currently not in the DOM because of `rendered="false"` can not be affected by directly selecting them in an `update`. However, they can be updated by selecting the parent. For example `update="@(#container :button)` will not work, but `update="@(#container)` will. Please correct me @BalusC if needed! :) – Daniel Szalay Nov 12 '14 at 12:19
  • 3
    @Daniel: this is not specific to PFS. You'd face exactly the same problem when using standard JSF client IDs. See also among others http://stackoverflow.com/questions/9010734/why-do-i-need-to-nest-a-component-with-rendered-some-in-another-component-w – BalusC Nov 14 '14 at 20:15
  • I'm trying to update some elements by their class from an ajax exception handler (Primefaces), but it would seem any attempt to select more than a single component by their id is met with a `Client side expression (PFS and @widgetVar) are not supported... expression "@(.my-class)"` message. Does anyone know whether this is possible at all? – Herick Jul 02 '15 at 14:35
  • 1
    Can we update 'myclass' programatically? I have tried the omnifcaes Ajax.update("@(.myclass)") and other variations with other methods, like the primefaces one, but didn't work. Is there a easy way to update a class programatically? – GarouDan Aug 05 '16 at 19:42
  • @GarouDan - Check out org.primefaces.SearchExpressionFacade.resolveClientIds(), which resolves a search expression into a list of component ids. You can update a component by its id programmatcially by invoking FacesContext.getPartialViewContext().getRenderIds().add() to add a component id to the list of ids that will get updated after the AJAX request. – jahroy Jul 28 '22 at 17:30
  • Does "@(.myclass)" also work for process="@(.myclass)" ? – raho Oct 27 '22 at 12:49