Iterate over enum values imported by <o:importConstants> - jsf

Is it possible to iterate over ENUM in ui:repeat or c:forEach ?
I'm using o:importConstants of Omnifaces 2.5.
Example code:
<o:importConstants type="my.package.MyEnum"></o:importConstants>
<c:forEach var="icon" items="#{MyEnum}">
#{icon.toString()}
</c:forEach>
but it comes:
com.sun.faces.facelets.tag.jstl.core.MappedValueExpression$Entry#565a5787
com.sun.faces.facelets.tag.jstl.core.MappedValueExpression$Entry#6c01f0ce
com.sun.faces.facelets.tag.jstl.core.MappedValueExpression$Entry#2cd6ac37
com.sun.faces.facelets.tag.jstl.core.MappedValueExpression$Entry#7b6d8d37
com.sun.faces.facelets.tag.jstl.core.MappedValueExpression$Entry#7f8f1bb2

The <o:importConstants> converts the enum values to a Map<String, E> where the map key is the string representation of the enum name and the map value is the actual enum instance itself. What you're essentially attempting right now is printing each Map.Entry instance as string. You should actually be using its getKey() and/or getValue() methods instead.
Iterating over a Map directly is as of now only supported in <c:forEach>. See also How to use jstl foreach directly over the values of a map?
<c:forEach items="#{MyEnum}" var="entry">
Map key: #{entry.key} <br/>
Map value: #{entry.value} <br/>
</c:forEach>
The <ui:repeat> (and <h:dataTable>) only supports it from JSF 2.3 on. Until then, you'd best iterate over Map#values() instead.
<ui:repeat value="#{MyEnum.values()}" var="value">
Map value: #{value} <br/>
</ui:repeat>

Related

How to get value from Map<TestClass,Object> in item label

Below is the jsf code
<h:selectOneMenu>
<f:selectItems value="#{bean.mapObject}"var="entity"
itemValue="#{entity.key}" itemLabel="#{entity.value.code}"/>
</h:selectOneMenu>
Backing bean has map defined as
private Map<TestClass, Object> mapObject;
TestClass has two variables and getters/setters
String code;
String name;
Issue I am having is : On JSF, on Selectonemenu, for itemLabel i would like to display the TestClass String value i.e code should be displayed.
Presently on SelectOneMenu on UI, I am getting
TestClass[code=t,name=anu] on the drop down, instead i want code value i..e 't'
Please let me know how to acheive this.
The <f:selectItems var> should in case of maps specify the Map.Entry. The <f:selectItems value> should therefore specify Collection<Map.Entry>. This is not natively recognized yet when you just specify a Map (will come in JSF 2.3).
You basically need to explicitly set the value to Map#entrySet() as below in case you intend to use map key as item value and ma:
<h:selectOneMenu>
<f:selectItems value="#{bean.mapObject.entrySet()}"var="entry"
itemValue="#{...}" itemLabel="#{...}" />
</h:selectOneMenu>
The Map.Entry object has getKey() and getValue() methods which return respectively the map entry's key and map entry's value, which are in your case respectively TestClass and Object. You seem to want to display the code property of TestClass as item label. So, this should do:
<h:selectOneMenu>
<f:selectItems value="#{bean.mapObject.entrySet()}"var="entry"
itemValue="#{...}" itemLabel="#{entry.key.code}" />
</h:selectOneMenu>
You're not terribly clear on what exactly you'd like to use as item value, so I've left it open.
See also:
Our selectOneMenu wiki page
You need to change the itemlabel attribute, (with current value entity.value.code). It should be replaced by entity.key.code ?
Also the value needs to call the method .entrySet , because the map in itself is not a collection.
<h:selectOneMenu>
<f:selectItems value="#{bean.mapObject.entrySet()}" var="entry"
itemValue="#{entry.key}" itemLabel="#{entry.key.code}"/>
</h:selectOneMenu>
Alternatively, since it's the key you want, you could iterate the keys immediately.
<h:selectOneMenu>
<f:selectItems value="#{bean.mapObject.keySet()}" var="key"
itemValue="#{key}" itemLabel="#{key.code}"/>
</h:selectOneMenu>

How to save h:inputText values of a h:dataTable? My attempt only saves the value of last row

I'm having trouble making a dataTable where each row has a inputText and a commandLink. When the link is clicked, only it's row's inputText's data is submitted.
Something like this?
<h:dataTable value="#{bean.items}" var="item">
<h:column>
<h:inputText value="#{bean.value}"/>
</h:column>
<h:column>
<h:commandLink action="#{bean.save}" value="save">
<f:setPropertyActionListener target="#{bean.item}" value="#{item}" />
</h:commandLink>
</h:column>
</h:dataTable>
Bean:
#RequestScoped
public class Bean {
private Item item;
private String value;
Right now, as it is, it's using the last row's inputText to fill the value. I wrapped another h:form, but it broke other things and I've learned that nested h:form is not the right way to do it hehe
What's the correct way to do this?
Thanks.
You're binding the value of all HTML input elements to one and same bean property. This is of course not going to work if all those HTML input elements are inside the same form. All values are subsequently set on the very same property in the order as the inputs appeared in the form. That's why you end up with the last value. You'd like to move that form to inside the <h:column> (move; thus don't add/nest another one).
The usual approach, however, would be to just bind the input field to the iterated object.
<h:inputText value="#{item.value}"/>
An alternative, if you really need to have your form around the table, is to have a Map<K, V> as bean property where K represents the type of the unique identifier of the object behind #{item} and V represents the type of value. Let's assume that it's Long and String:
private Map<Long, String> transferredValues = new HashMap<Long, String>();
// +getter (no setter necessary)
with
<h:inputText ... value="#{bean.values[item.id]}" />
This way you can get it in the action method as follows:
String value = values.get(item.getId());
By the way, if you happen to target Servlet 3.0 containers which supports EL 2.2 (Tomcat 7, Glassfish 3, etc), then you can also just pass the #{req} as a method argument without the need for a <f:setPropertyActionListener>.
<h:commandLink ... action="#{bean.save(item)}" />
See also:
How and when should I load the model from database for h:dataTable
How can I pass selected row to commandLink inside dataTable?
How to dynamically add JSF components

JSF: logic based on iteration index

Pardon me for the title, that's the best my limited brain can came up this late.
So, i have a list of string, something like [abc, def, ghi].
The question: in JSF, how do I iterate the list and create a string that look like this "abc, def, ghi" (notice the commas)?
For those who have the urge to tell me that I'd better use a Java method to concatenate the string, hear this: every member of the list should be rendered as a separate commandLink.
If plain JSF it would look like:
<h:commandLink>abc</h:commandLink>, <h:commandLink>def</h:commandLink>, <h:commandLink>ghi</h:commandLink>
Assuming that #{bean.items} returns List<String> or String[], in JSF 1.x you can use JSTL c:forEach with varStatus. It gives you a handle to LoopTagStatus which has a isLast() method.
<c:forEach items="#{bean.items}" var="item" varStatus="loop">
<h:commandLink value="#{item}" /><c:if test="#{!loop.last}">, </c:if>
</c:forEach>
In Facelets as shipped with JSF 2.x, same functionality is available by ui:repeat.
<ui:repeat value="#{bean.items}" var="item" varStatus="loop">
<h:commandLink value="#{item}" />#{!loop.last ? ', ' : ''}
</ui:repeat>

In JSF what is the shortest way to output List<SomeObj> as comma separated list of "name" properties of SomeObj

I have a question about outputing a list of objects as a comma separated list in JSF.
Let's say:
public class SomeObj {
private String name;
... constructors, getters and setters ...
}
and List<SomeObj>:
List<SomeObj> lst = new ArrayList<SomeObj>();
lst.add(new SomeObj("NameA"));
lst.add(new SomeObj("NameB"));
lst.add(new SomeObj("NameC"));
to output it as a listbox I can use this code:
<h:selectManyListbox id="id1"
value="#{listHolder.selectedList}">
<s:selectItems value="#{listHolder.lst}"
var="someObj"
label="#{someObj.name}"/>
<s:convertEntity />
</h:selectManyListbox>
But what is the easiest way to output the list as is, comma seperated ? Like this:
NameA, NameB, NameC
Should I use JSTL <c:forEach/> or may be the <s:selectItems/> tag can also be used ?
Given a List<Person> persons where Person has a name property,
If you're already on Java EE 7 with EL 3.0, then use EL stream API.
#{bean.persons.stream().map(p -> p.name).reduce((p1, p2) -> p1 += ', ' += p2).get()}
If you're not on EL 3.0 yet, but have JSF 2.x at hands, then use Facelets <ui:repeat>.
<ui:repeat value="#{bean.persons}" var="person" varStatus="loop">
#{person.name}#{not loop.last ? ', ' : ''}
</ui:repeat>
Or if you're still on jurassic JSP, use JSTL <c:forEach>.
<c:forEach items="#{bean.persons}" var="person" varStatus="loop">
${person.name}${not loop.last ? ', ' : ''}
</c:forEach>
See also:
How iterate over List<T> and render each item in JSF Facelets
JSTL in JSF2 Facelets... makes sense?
use <ui:repeat> (from facelets). It's similar to c:forEach
Or pre-compute the comma-separated string in the managed bean, and obtain it via a getter.
If you can't use varStatus because you're stuck with using JSF 1.2, you can do:
<ui:repeat value="#{listHolder.lst}" var="someObj">#{someObj != listHolder.lst[0] ? ',' : ''}
#{someObj.name}</ui:repeat>
The absence of whitespace around the EL-expressions is deliberate, we don't want a space to appear there in the rendered HTML.

How to dynamically create a <f:selectItem> list?

Is there a way to dynamically create a selectItem list? I dont really want to have to create lots of bean code to make my lists return List<SelectItem>.
I tried this:
<ice:selectManyCheckbox>
<ui:repeat var="product" value="#{productListingService.list}">
<f:selectItem itemLabel="#{product.description}" value="#{product.id}"/>
</ui:repeat>
</ice:selectManyCheckbox>
but it doesnt work.
Any ideas?
Use <f:selectItems> instead. It accepts next to List<SelectItem> and SelectItem[] also a Map<String, Object> as value where the map key is the item label and map value is the item value. Or if you're already on JSF 2.0, then you can use a List<SomeBean> instead where the current item can be referenced by the var attribute.
<f:selectItems value="#{productListingService.list}" var="product"
itemLabel="#{product.description}" itemValue="#{product.id}" />
See also:
Our <h:selectOneMenu> wiki page

Resources