How can I set numbers within IDs dynamically in JSF - jsf-2

I design a JavaEE website with the help of JSF2 and Facelets. The code beyond is just an example how the JSF-Code looks like.
When the user of the website wants to create new users but forgets to set e.g. the firstname, an error occurs and the element will be highlighted (red color and red frame around the element).
For highlighting I have to use the attribute:
class="#{faceletUtils.getLabelStyleClass('createUsersForm:allUsers:0:firstName')}"
The problem is that I have to set the number 0 dynamically but as far as I know nesting is not allowed in JSF. I could use #{curUser.number} to get the number.
Question: How can I set the numbers in the for and class attributes?
for="createUsersForm:allUsers:0:firstName"
class="#{faceletUtils.getLabelStyleClass('createUsersForm:allUsers:0:firstName')}"
An example of the JSF-Code:
<h:form id="createUsersForm">
<ui:repeat id="allUsers" value="#{createUserController.users}" var="curUser">
<div class="formLine">
<label for="createUsersForm:allUsers:0:firstName" class="#{faceletUtils.getLabelStyleClass('createUsersForm:allUsers:0:firstName')}">
<h:outputText value="#{userStaticText['user.firstName.label']}"/>
</label>
<h:inputText id="firstName" value="#{curUser.firstName}" class="#{faceletUtils.getLabelStyleClass('createUsersForm:allUsers:0:firstName')}"/>
</div>
</ui:repeat>
</h:form>
Best regards,
Jana

Just use <h:outputLabel> instead of <label>. JSF will automatically use the right for value. Further you can use #{component.clientId} to get the client ID of the current component (although I think the particular functional requirement of highlighting invalid fields can be solved in a much simpler manner).
<h:form id="createUsersForm">
<ui:repeat id="allUsers" value="#{createUserController.users}" var="curUser">
<div class="formLine">
<h:outputLabel for="firstName" styleClass="#{faceletUtils.getLabelStyleClass(component.clientId)}"
value="#{userStaticText['user.firstName.label']}"/>
<h:inputText id="firstName" value="#{curUser.firstName}" styleClass="#{faceletUtils.getLabelStyleClass(component.clientId)}"/>
</div>
</ui:repeat>
</h:form>
Note that I fixed the wrong class attribute of <h:inputText> as well.

Related

Replace <c:if> and fn:toUpperCase() with JSF-tags

This example is from a book on JSF. The excercise is to refactor the following Facelets code while eliminating <c:if> and fn:toUpperCase(). Usage of <c:forEach> is allowed.
#{myBean.numbers} returns String["one","two","three"]
As the book is on JSF and not on Java, I suppose the existing Java-code is not to be touched. But I can't think of another way to do this solely in Facelets.
<c:forEach var="item" items="#{myBean.numbers}">
<c:if test="#{not fn:endsWith(item,'o')}">
#{item}
</c:if>
<c:if test="#{fn:endsWith(item,'o')}">
#{fn:toUpperCase(item)}
</c:if>
</c:forEach>
Only thing I can think of is using a converter that conditionally uses String#toUpperCase() and then I still do not understand why use of <c:forEach> should still be allowed:
<ui:repeat var="item" value="#{myBean.numbers}">
<h:outputText value="#{item}" converter="conditionalConverter"/>
</ui:repeat>
Is there a more "Facelets way" to do this (and still a need to use <c:forEach>)?
UPDATE:
Instead of <c:if> one could still use e.g. <h:outputPanel> and it's rendered-attribute, but there is still no Java-less replacement for fn:toUpperCase().
I am asking for learning purposes only. I suppose the <ui:repeat>-solution with a converter is the cleanest and represents most how JSF is supposed to be used. Do you think so, too?
As to <c:if>, the JSF alternative to JSTL <c:if> is the rendered attribute on any component. For example, <h:panelGroup> or <h:outputText>. Those components doesn't generate additional markup if there are no attribtues specified which should end up in HTML, like id or styleClass, otherwise they generate a <span>.
Here's an example of both:
<h:panelGroup rendered="#{not fn:endsWith(item,'o')}">
#{item}
</h:panelGroup>
<h:outputText value="#{fn:toUpperCase(item)}" rendered="#{fn:endsWith(item,'o')}" />
As to fn:toUpperCase(), JSF has no alternative. I'm not sure why you would need a JSF alternative as it's essentially not a tag, but a simple EL function which is perfectly usable in both JSTL and JSF tags. In any case, you could if necessary throw in CSS text-transform: uppercase. As this takes place entirely client side, your only problem may be the browser support.
<h:outputText value="#{item}" style="text-transform: uppercase" />
(note: this is just an example, the normal practice is to put styles in its own .css file which you load by <h:outputStylesheet>)
<h:outputText value="#{item}" styleClass="uppercased" />
I suppose the -solution with a converter is the cleanest and represents most how JSF is supposed to be used. Do you think so, too?
I'm a big fan of "Use the right tool for the job". Use JSTL tags to conditionally build the JSF component tree. Use JSF components to generate HTML. That's it. See also JSTL in JSF2 Facelets... makes sense?

Dynamically added component are growing vertically

I am following the option suggested here to dynamically adding inputtext box in my PrimeFaces v3.5 application. The only problem I am facing is that the newly added inputtext are growing vertically. Screenshot is attached.
The <h:dataTable> generates a HTML <table> element wherein each iteration generates a <tr> element which represents naturally a new row.
If you don't want to generate a table at all, then you should not be using a <h:dataTable>, but e.g. <ui:repeat>.
<h:form>
<ui:repeat value="#{bean.items}" var="item">
<h:inputText value="#{item.value}" />
</ui:repeat>
<h:commandButton value="add" action="#{bean.add}" />
<h:commandButton value="submit" action="#{bean.submit}" />
</h:form>
This doesn't generate any markup and the HTML <input> elements will by default end up in a single line.

JSF2 <ui:param give wrong value when used in <h:panelGroup

I have 2 <h:panelGroup for conditional text display. When I run with id empty than this condition is executed fine #{empty personBean.person.id} and I see text in it. If I put <h2>#{buttonText}</h2> in panels than I see buttonText properly displayed in h2 tag.
Now Problem:
In below code if I put <h2>#{buttonText}</h2> at the end like below than I always get value Update member due to this I can not use value of buttonText anywhere in the page below. Someone tell me what to do?
<h:panelGroup rendered="#{empty personBean.person.id}">
<h1>Add Information</h1>
<i>Use the form below to add your information.</i>
<ui:param name="buttonText" value="Add member" />
</h:panelGroup>
<h:panelGroup rendered="#{not empty personBean.person.id}">
<h1>Update Information</h1>
<i>Use the form below to edit your information.</i>
<ui:param name="buttonText" value="Update member" />
</h:panelGroup>
<h2>#{buttonText}</h2>
I am unable to use <ui:param like I use <c:set var="buttonText" value="Add member" /> JSTL set variable.
The <ui:param> is not evaluated during view render time (ask yourself; does it necessarily generate any HTML?), but during view build time. So the rendered attribute isn't been taken into account at all and you effectively end up with both being interpreted and the latter one would always override the former one.
Technically, you'd need to "render" ("build" is a better term, but it reads a bit strange) it conditionally using a view build time tag such as <c:if>. However, in your particular construct it's better is to just check the very same condition in <ui:param>'s value and have only one of it instead of two:
<ui:param name="buttonText" value="#{empty personBean.person.id ? 'Add' : 'Update'} member" />
or just evaluate it directly there where you need it
<h2>#{empty personBean.person.id ? 'Add' : 'Update'} member</h2>
See also:
JSTL in JSF2 Facelets... makes sense? - not exactly your issue, but just to understand "view build time" versus "view render time" better.

JSF not firing bean setters when using f:ajax

This is an update on a previous post I made regarding conditional rendering of page components. I can now conditionally render different components on the page based on selected user inputs using the f:ajax tag. The trouble is that when I click my commandbutton the inputs that are conditionally rendered using ajax aren't being read on submit. I am guessing it is because the whole page isn't rerendered when I add in the new components and the submit button can't see them, even though they are there. Here is the segment of my form page(it is wrapped in a h:form tag):
<h:outputLabel for="selectPersonType" value="Select Type: "/>
<h:selectOneMenu id="selectPersonType" value="#{addPersonBean.personType}" label="Person Type" required="true">
<f:selectItem itemValue=""/>
<f:selectItems value="#{listBean.personTypes}" />
<f:ajax render="results"/>
</h:selectOneMenu>
<h:message for="selectPersonType" style="color: red"/>
</h:panelGrid>
<h:panelGroup id="results">
<h:panelGroup rendered="#{addPersonBean.personType == 'STUDENT'}">
<h:outputLabel for="selectSchoolType" value="School Type: " />
<h:selectOneMenu id="selectSchoolType" value="#{addPersonBean.schoolType}">
<f:selectItems value="#{listBean.schoolTypes}" />
<f:ajax execute="selectSchoolType"/>
</h:selectOneMenu>
</h:panelGroup>
<h:panelGroup rendered="#{addPersonBean.personType == 'PATIENT'}">
<h:outputLabel for="smoker" value="Smoker? " />
<h:selectBooleanCheckbox id="smoker" value="#{addPersonBean.smoker}">
<f:ajax execute="smoker"/>
</h:selectBooleanCheckbox>
</h:panelGroup>
</h:panelGroup>
<h:commandButton value="Add Person" action="#{addPersonBean.action}"/>
The trouble here is that the components with ids 'selectSchoolType' and 'smoker's values don't get set when I click the commandButton because they are rendered conditionally using the selectPersonType select menu after the page has loaded. Is there a way to fire the components when their values change instead of when I click the submit button (therefore, their values should be processed before I even click submit). As you can see I have tried to use f:ajax with the execute attribute attached to the components in question but it didn't seem to work. (I believe the default events for these components are valueChange so haven't added them to the f:ajax tags, but I have tried 'change' and 'valueChange').
All your <f:ajax> things are inside a conditionally rendered component. This construct will fail when the condition behind rendered attribute evaluates to false during processing the form submit. This will happen when the #{addPersonBean} is in the request scope and doesn't preinitialize the condition behind the rendered attribute during (post)construction, or when it's performing business logic in getters/setters in a wrong way.
Placing the #{addPersonBean} in the view scope and ensuring that you aren't doing business logic in getters/setters should fix this problem.
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated - point 5 applies to you
How to choose the right bean scope?

Can I add html to a jsf h:message with a custom component?

I would like to add a header and footer image to a h:message in JSF 2.0 so it has nice borders.
It looks like the way to do this is to implement a custom renderer for a component and write out the tags. Id like to do this as a composite component but I cant figure out how to say 'dont draw the header and footer unless youre drawing the message'.
Is there a way to do this with a composite component?
Im using mojarra.
You can use FacesContext#messageList(String clientId) to get a List<FacesMessage> with all messages for a specific client ID (of which you'd usually be interested in only the first one, you can change the below example if you want). So you could just check in the rendered attribtue if the list is not empty. Then you can use <h:outputText escape="false"> to display the message without implicitly escaping HTML. You could if necessary wrap it all in a composite to keep your code DRY.
<h:inputText id="input1" binding="#{input1}" value="#{bean.input1}" />
<ui:param name="input1Messages" value="#{facesContext.messageList(input1.clientId)}" />
<h:panelGroup rendered="#{not empty input1Messages}">
<h3>Some header</h3>
<p><h:outputText value="#{input1Messages[0].summary}" escape="false" /></p>
<p>Some footer</p>
</h:panelGroup>
Note that this method was not available in JSF 1.x, that's why you'd need to create a custom component for it.

Resources