How do I break the tyranny of the clientID? - jsf-2

My least favorite part of coding JSF 2.0 forms has to do with the handing of the id attributes of the various input elements. I am forever having trouble coding the clientID of the target component from within the backing bean, particularly since PrimeFaces tabView now includes the id of the p:tab element as part of the clientID. I waste tons of time coding, testing, then re-coding those clientIDs.
It is reminiscent of older-style assembly language programming where you have to generate tons of label names for your branches and loops. I've done of enough of that for a lifetime.
One approach I am trying is to use only auto-generated id attributes. For example one line of my form might look like this.
<h:outputLabel value="Full Name:" />
<p:inputText value="#{editUser.user.fullName}"
binding="#{editUser.compFullName}"/>
<p:message for="#{editUser.compFullName.clientId}" />
Note that I do not have an explicit id attribute. Then in the backing bean:
String clientID = getCompFullName().getClientId();
msg = new FacesMessage(FacesMessage.SEVERITY_INFO,
"Summary Message For Full Name", "Detail Message Full Name");
FacesContext.getCurrentInstance().addMessage(clientID, msg);
This always works, even if the component has a complex clientID, such as when PrimeFaces inserts the p:tab id into the clientID. (Which it does starting v 3). Rearranging the form never breaks anything.
It is, however, laborious, since I have to create UIComponent properties, getters and setters, and bind them in the form with binding attributes. Can anyone suggest a better way of doing this?

since I have to create UIComponent properties, getters and setters, and bind them in the form with binding attributes. Can anyone suggest a better way of doing this?
It's not required to bind the component to some backing bean if you don't use it in there at all. Just bind it to the view instead:
<p:inputText value="#{editUser.user.fullName}"
binding="#{compFullName}"/>
<p:message for="#{compFullName.clientId}" />
To make the code more self-documenting, I suggest to put a HashMap in the request scope by faces-config.xml:
<managed-bean>
<description>Holder of all component bindings.</description>
<managed-bean-name>components</managed-bean-name>
<managed-bean-class>java.util.HashMap</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
with
<p:inputText value="#{editUser.user.fullName}"
binding="#{components.fullName}"/>
<p:message for="#{components.fullName.clientId}" />
Adding messages is supposed to be done by a Converter or a Validator which is trowing it as a ConverterException or ValidatorException respectively. It will automatically end up in the right message holder. Or if it are informal messages, just add it on the client ID of the UIComponent which is already available as method argument.
See also:
JSF component binding without bean property

Related

c:forEach not working on ace:dataTable var [duplicate]

In my application I want to display a <h:dataTable> with managed bean properties. Currently this table is created from a List<Folder>. Now I want to change the Folder to something more dynamic. That's because I don't want to change the Folder class if I decide to add another field later. I would just have to add another entry in the Map<String, Object> instead of introducing a new field in Folder.
So, is it possible to bind a List<Map<String, Object>> to the <h:dataTable>?
Is it possible to bind a List of HashMaps to the jsf component h:dataTable?
That's only possible if you generate the necessary <h:column> tags with a view build time tag such as JSTL <c:forEach>.
Here's a concrete kickoff example, assuming that your environment supports EL 2.2:
<h:dataTable value="#{bean.listOfMaps}" var="map">
<c:forEach items="#{bean.listOfMaps[0].keySet().toArray()}" var="key">
<h:column>
#{map[key]}
</h:column>
</c:forEach>
</h:dataTable>
(if your environment doesn't support EL 2.2, you'd need to provide another getter which returns the map key set as a String[] or List<String>; also keep in mind that a HashMap is by nature unordered, you might want to use LinkedHashMap instead to maintain insertion order)
When you're using Mojarra version older than 2.1.18, the disadvantage is that the #{bean} has to be request scoped (not view scoped). Or at least, the <c:forEach items> should refer a request scoped bean. A view scoped bean would otherwise be recreated on every single HTTP request as the <c:forEach> runs during view build time, when the view scope isn't availabe yet. If you absolutely need a view scoped bean for the <h:dataTable>, then you can always create a separate request scoped bean exclusively for <c:forEach items>. The solution would be to upgrade to Mojarra 2.1.18 or newer. For some background information, see also JSTL in JSF2 Facelets... makes sense?
JSF component libraries such as PrimeFaces may offer a <x:columns> tag which makes this more easy, such as <p:dataTable> with <p:columns>.
<p:dataTable value="#{bean.listOfMaps}" var="map">
<p:columns value="#{bean.listOfMaps[0].keySet().toArray()}" var="key">
#{map[key]}
</p:columns>
</p:dataTable>

jsf 2.2 enable disable button with EL and not javascript [duplicate]

I have an inputField, or some other tag , that I want to be disabled unitl user clicks on it.
Something like this, but I cant get it to work in JSF.
$("div").click(function (evt) {
$(this).hide().prev("input[disabled]").prop("disabled", false).focus();
});
I add disabled=true" to my input field, and div value set on < h:form > (all parent tags in this case only one), something like j_idt13 and div of input field, so "div" value looks like j_idt13:inputID
Can someone help me with jQuery solutin?
I wold like to know can it be done in JSF, and how.
You need to toggle it via server side, not via client side. JSF as being a stateful component based MVC framework safeguards this way against tampered/hacked requests wherein the enduser uses client side languages/tools like HTML or JS to manipulate the HTML DOM tree and/or HTTP request parameters in such way that the outcome of JSF's disabled, readonly or even rendered attribute is altered.
Imagine what would happen if the JSF developer checked an user's role in such a boolean attribute against the admin role like so disabled="#{not user.hasRole('ADMIN')}" and a hacker manipulated it in such way that it isn't disabled anymore for non-admin users. That's exactly why you can only change the mentioned attributes (and the required attribute and all converters, validators, event listeners, etc) via the server side.
You can use <f:ajax> in any ClientBehaviorHolder component to achieve the requirement. You can let JSF generate a HTML <div> via <h:panelGroup layout="block">, which is also a ClientBehaviorHolder:
<h:form>
<h:panelGroup layout="block">
Click this div to toggle the input.
<f:ajax event="click" listener="#{bean.toggle}" render="input" />
</h:panelGroup>
<h:inputText id="input" ... disabled="#{not bean.enabled}" />
</h:form>
With this #ViewScoped managed bean (#RequestScoped wouldn't work for reasons mentioned in #5 of commandButton/commandLink/ajax action/listener method not invoked or input value not updated):
#Named
#ViewScoped
public class Bean implements Serializable {
private boolean enabled;
public void toggle() {
enabled = !enabled;
}
public boolean isEnabled() {
return enabled;
}
}
See also:
What is the need of JSF, when UI can be achieved from CSS, HTML, JavaScript, jQuery?
Why JSF saves the state of UI components on server?
Unrelated to the concrete problem, head to the following answers in case you're actually interested in how to obtain the HTML representation of JSF components via JS/jQuery:
How to select JSF components using jQuery?
How can I know the id of a JSF component so I can use in Javascript

evaluate jsf bean property based on URL

Is there a way to display a specific JSF page based on the request URL?
Let's say I have a JSF page "details.xhtml". The managed bean "detailsBean" has a list of objects where each object has its own ID. Now if a user requests the page "../details.xhtml?id=1", the list should be queried for an object with ID 1 and the resulting details page of this object should be displayed.
I already wrote a converter implementation class which can convert from object to ID and vice versa, but I don't know how to use it properly. Do I have to work through the JAX-RS specification for this to work or is there a more simple solution?
In JSF you can do this by using a so-called view parameter. You declare these in the metadata section of your Facelet:
<f:metadata>
<f:viewParam name="id" value="#{yourBean.yourObject}" label="id"
converter="yourObjectConverter"
/>
</f:metadata>
This will grab the URL parameter id from the request URL. E.g. if you request the page this appears on with localhost:8080/mypage.jsf?id=1, then 1 will be handed to the yourObjectConverter and whatever this converter returns will be set in yourBean.yourObject.
Your backing bean will thus get the converted object. No need to pollute your backing bean over and over again with the same query code.
#ManagedBean
public class YourBean {
private SomeObject someObject;
public void setYourObject(SomeObject someObject) {
this.someObject = someObject;
}
}
If your backing bean is view scoped, you may want to use the OmniFaces variant of viewParam instead, since otherwise it will needlessly convert after each postback (if your converter does a DB query, you definitely don't want this).
Working full examples:
http://code.google.com/p/javaee6-crud-example/source/browse/WebContent/user_edit.xhtml
http://code.google.com/p/javaee6-crud-example/source/browse/src/backing/UserEdit.java
Further reading:
Communication in JSF 2.0 - Processing GET request parameters
Stateless vs Stateful JSF view parameters
You can achieve this with plain JSF with the following steps
Capture the ID in the request to determine what object is being queried for in your DetailsBean from the request parameter. There are many ways to achieve this, one of which is adding the following annotation to your managed bean (this is currently only permitted for a #RequestScoped bean, see why here).
#ManagedProperty(value="#{param.id}")
int requiredObjectId;
The annotation above will capture the id parameter from the request and assign it to the requiredObjectId variable.
Using the captured Id, setup your object in your bean in a #PostConstruct method
#PostConstruct
public void queryForObject(){
//use the requiredObjectId variable to query and setup the object in the backing bean
}
The object retrieved should be assigned as an instance variable of your managed bean
In your view, you could then reference the queried object that has been setup in the backing bean
<h:panelGrid columns="2">
<h:outputText value="Title"/>
<h:outputText value="#{detailsBean.selectedObject.title}"/>
</h:panelGrid>
If your bean is in a scope broader than the request scope, you'll need a combination of constructs to cleanly pull that request parameter before view rendering.
Capture the request parameter within the JSF view itself using
<f:metadata>
<f:viewParam name="id" value="#{detailsBean.requiredObjectId}" required="true" requiredMessage="You must provide an Object Id"/>
</f:metadata>
**OR**
Due to the nature of JSF Lifecycle processing, doing the above alone may not make the value available for your use in time for object setup. You could use the following instead.
<f:metadata>
<f:event type="preRenderView" listener="#{detailsBean.setObjectId}" />
</f:metadata>
What we've done here is specify a method (that captures the id) in the backing bean that must be executed before the view is rendered, ensuring that the id parameter is available as at the time you need it. Proceed with step 3, only if you're using <f:event/> above.
In the backing bean, you now define the setObjectId method
public void setObjectId(){
Map<String,String> requestParams = FacesContext.getExternalContext().getRequestParameterMap();
requiredObjectId = Integer.parseInt(requestParams.get("id"));
}
Note that the above option is generally a work around/hack and not a clean solution as such

Primefaces ManyCheckbox inside ui:repeat calls setter method only for last loop

I have a <p:selectManyCheckbox> inside <ui:repeat>, getting it's items from a List of a certain Object Class (provided by <ui:repeat>-variable) and is supposed to save the chosen items into another List of the same Object Class. But it calls the setter method #{cartBean.setSelectedExtras} only for the last entry (last iteration of <ui:repeat>).
<ui:repeat var="item" value="#{category.items}">
<p:selectManyCheckbox id="extraCheckbox" value="#{cartBean.selectedExtras}" layout="pageDirection" converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{item.items5}" var="extra" itemLabel="#{extra.name}"/>
</p:selectManyCheckbox>
</ui:repeat>
Update:
I changed the above construct just the way BalusC proposed.
Declaration in backing bean is now:
private List<List<Item>> selectedExtras = new ArrayList<List<Item>>();
When I check checkboxes that were created by the first loops of <ui:repeat> and click the <p:commandButton> inside the same <h:form> the setter method of selectedExtras is not called. When I check the checkboxes created in the last loop of <ui:repeat> and click the <p:commandButton> I get an Exception:
javax.el.PropertyNotFoundException: /lightbox-item.xhtml #57,165 value="#{cartBean.selectedExtras[iteration.index]}": null
This construct works fine for me.
As mentioned in among others the showcase page, the omnifaces.SelectItemsConverter uses by default the toString() representation of the complex object as converted item value. So if you didn't override the toString() method (so that it still defaults to com.example.SomeClass#hashcode which changes on every instantiation) and the #{item} managed bean is request scoped, then the list would basically be changing on every HTTP request. This would cause a "Validation Error: Value is not valid" error.
If you add
<p:messages autoUpdate="true" />
or
<p:growl autoUpdate="true" />
so that you get all (missing) validation/conversion messages in the UI, then you should have noticed it.
In order to utilize the omnifaces.SelectItemsConverter at its best, you should override the toString() method accordingly so that it returns a fixed and unique representation of the complex object. E.g.
#Override
public String toString() {
return "Extra[id=" + id + "]";
}
Alternatively, you could put the #{item} managed bean in a broader scope, such as the view scope.
Update as to your update, you're binding the selected values of all checkboxgroups to one and same bean property #{cartBean.selectedExtras}. This way every iteration overrides the property with the values from the current iteration round as long as until you end up with the values of the last iteration. If you've placed a debug breakpoint on the setter, you'd have noticed that.
This is not right. They should each point to a different bean property. Technically, you should have a #{item.selectedExtras} as property. But I think that this makes no sense in your current design. Better would be to make the #{cartBean.selectedExtras} an List<Item[]> or Item[][]. This way you can get them to set based on the iteration index as follows:
<ui:repeat var="item" value="#{category.items}" varStatus="iteration">
<p:selectManyCheckbox id="extraCheckbox" value="#{cartBean.selectedExtras[iteration.index]}" layout="pageDirection" converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{item.items5}" var="extra" itemLabel="#{extra.name}"/>
</p:selectManyCheckbox>
</ui:repeat>
In case of List<Item[]> you only need to make sure that you preinitialize selectedExtras with nulls as many times as there are #{category.items} in bean's (post)constructor.
for (Item item : category.getItems()) {
selectedExtras.add(null);
}
In case of Item[][], you can suffice with
selectedExtras = new Item[category.getItems().size()];

inputHidden binding to a long id field in backing bean

Just wondering how to set a hidden field value so that when I submit my form, JSF sets it as the id in an object in my CDI-managed bean.
My bean is called "discussionManager" and it has an object in it called 'discussion', which is an entity and therefore has an ID of type Long.
I need the ID so I can look it up and do stuff with it. But, JSF doesn't seem to like numeric hidden fields. It is fine with string fields though. Sure it has something to do with converters or the binding attribute, but can't get the syntax. This is what I'm trying in it's simplest form.
<h:inputHidden id="discussionId" value="#{viewDiscussionBean.discussion.id}"/>
I've tried lots of variations. Can anyone point me in the right direction please?
Thanks
You indeed need to explicitly specify a converter. The JSF builtin LongConverter is suitable.
<h:inputHidden id="discussionId" value="#{viewDiscussionBean.discussion.id}" converter="javax.faces.Long" />

Resources