UIForm with prependId="false" breaks <f:ajax render>
Asked Answered
W

1

21

I have a question about the idea behind the fact, that only UIForm got the attribute prependId. Why is the attribute not specified in the NamingContainer interface? You will now probably say that's because of backward compability but I would preferre breaking the compability and let users which implement that interface, also implement methods for the prependId thing.

The main problem from my perspective about the prependId in the UIForm component is, that it will break findComponent() I would expect that if I use prependId, then the NamingContainer behaviour would change, not only related to rendering but also when wanting to search for components in the component tree.

Here a simple example:

<h:form id="test" prependId="false">
  <h:panelGroup id="group"/>
</h:form>

Now when i want to get the panelGroup component I would expect to pass the string "group" to the method findComponent(), but it won't find anything, I have to use "test:group" instead.

The concrete problem with that is, when using ajax with prependId="false". The ajax tag expects in the attributes update and process, that the values care of naming containers. It's a bit strange that when I use prependId="false" that I have to specify the full id or path, but okay.

<h:form id="test" prependId="false">
  <h:panelGroup id="group"/>
</h:form>
<h:form id="test1" prependId="false">
  <h:commandButton value="go">
    <f:ajax render="test:group"/>
  </h:commandButton>
</h:form>

Well this code will render without problems but it won't update the panelGroup because it cannot find it. The PartialViewContext will contain only the id "group" as element of the renderIds. I don't know if this is expected, probably it is but I don't know the code. Now we come to the point where the method findComponent() can not find the component because the expression passed as parameter is "group" where the method would expect "test:group" to find the component.

One solution is to write your own findComponent() which is the way I chose to deal with this problem. In this method i handle a component which is a NamingContainer and has the property prependId set to false like a normal UIComponent. I will have to do that for every UIComponent which offers a prependId attribute and that is bad. Reflection will help to get around the static definition of types but it's still not a really clean solution.

The other way would be introducing the prependId attribute in the NamingContainer interface and change the behaviour of findComponent() to work like described above.

The last proposed solution would be changing the behaviour of the ajax tag to pass the whole id, but this would only solve the ajax issue and not the programmatic issues behind the findComponent() implementation.

What do you think about that and why the hell is it implemented like that? I can't be the first having this problem, but I wasn't able to find related topics?!

Woolard answered 14/9, 2011 at 10:59 Comment(1)
A beautifully written case by a programmer who indeed knows on how to reveal things. Thank you Christian.Bastard
L
19

Indeed, UIComponent#findComponent() as done by <f:ajax render> fails when using <h:form prependId="false">. This problem is known and is a "Won't fix": JSF spec issue 573.

In my humble opinion, they should never have added the prependId attribute to the UIForm during the JSF 1.2 ages. It was merely done to keep j_security_check users happy who would like to use a JSF form with JSF input components for that (j_security_check requires exact input field names j_username and j_password which couldn't be modified by configuration). But they didn't exactly realize that during JSF 1.2 another improvement was introduced which enables you to just keep using <form> for that instead of sticking to <h:form>. And then CSS/jQuery purists start abusing prependId="false" to avoid escaping the separator character : in their poorly chosen CSS selectors.

Just don't use prependId="false", ever.

For j_security_check, just use <form> or the new Servlet 3.0 HttpServletRequest#login(). See also Performing user authentication in Java EE / JSF using j_security_check.

For CSS selectors, in case you absolutely need an ID selector (and thus not a more reusable class selector), simply wrap the component of interest in a plain HTML <div> or <span>.

See also:

Lacteous answered 14/9, 2011 at 13:4 Comment(1)
Well i face another "problem" with the findComponent() algorithm or is it something different. When i try to update a specific row of a dataTable outside of it, then the component id can not be found. I use something like this: <f:ajax render="dataTableId:0:subElement"/> why can the ajax tag not resolve the component? I checked the source but had no clue why it does not work. I should take the parent NamingContainer as base for the lookup and then find the dataTable, iterate to the row and finally find the subElement but it does not! Ajax tag is a bit strange...Woolard

© 2022 - 2024 — McMap. All rights reserved.