I would like to display HashMap keys and its associated value in the JSF UI.
How can I achieve this? How can I iterate over a HashMap in JSF page using some iterator component like <h:datatable>?
Only <c:forEach> supports Map. Each iteration gives a Map.Entry instance back (like as in a normal Java for loop).
<c:forEach items="#{yourBean.map}" var="entry">
<li>Key: #{entry.key}, value: #{entry.value}</li>
</c:forEach>
The <h:dataTable> (and <ui:repeat>) only supports List (JSF 2.2 will come with Collection support). You could copy all keys in a separate List and then iterate over it instead and then use the iterated key to get the associated value using [] in EL.
private Map<String, String> map;
private List<String> keyList;
public void someMethodWhereMapIsCreated() {
map = createItSomeHow();
keyList = new ArrayList<String>(map.keySet());
}
public Map<String, String> getMap(){
return map;
}
public List<String> getKeyList(){
return keyList;
}
<h:dataTable value="#{yourBean.keyList}" var="key">
<h:column>
Key: #{key}
</h:column>
<h:column>
Value: #{yourBean.map[key]}
</h:column>
</h:dataTable>
Noted should be that a HashMap is by nature unordered. If you would like to maintain insertion order, like as with List, rather use LinkedHashMap instead.
With <ui:repeat> (JSF 2.2 and onwards):
<ui:repeat var="entry" value="#{map.entrySet().toArray()}">
key:#{entry.key}, Id: #{entry.value.id}
</ui:repeat>
Source: https://gist.github.com/zhangsida/9206849
Related
I'm trying to iterate through a List of Map items, i.e. an ArrayList of HashMaps or something similar, and I'm trying to do this in primefaces datatable. This is basically what I'm trying to do:
<body>
<h:form>
<p:dataTable value="#{customerBean.list}" var="map">
<c:forEach items="#{map}" var="entry">
<p:column headerText="#{entry.key}">
#{entry.value}
</p:column>
</c:forEach>
</p:dataTable>
</h:form>
</body>
In this case, customerBean.list is a List<Map<String, String>> and entry is a Map<String, String>.
What I want to do, is create a dynamic amount of columns, based on the amount of entries in a Map<String, String> while using the map entry's key as a header name, and the value as the output. The c:forEach thing seems to work fine when I'm using a hardcoded Map<String, String>, but apparently it can't loop through the var of the p:dataTable. I assume that the program takes precaution to avoid having to loop through Maps of different sizes. So how can I make this work anyway? How can I create an arbitrary amount of columns based on the amount of entries in a Map? Note that I'm a 100% certain that every Map<String, String> is of equal size in my List<Map<String, String>>
EDIT:
Here's my bean source. The code works fine and everything, the problem is just with the loop not willing to go through my map:
#ManagedBean
#SessionScoped
public class CustomerBean {
private List<Map<String, String>> list = new ArrayList<Map<String, String>>();
private Mapper mapper = new Mapper();
public CustomerBean() {
list = mapper.all(); //gets data from database
}
public List<Map<String, String>> getList() {
return list;
}
public void setList(List<Map<String, String>> list) {
this.list = list;
}
}
The problem is unrelated to the Map usage in this context. The problem is that you're trying to get a #{map} variable that's only available when view is being rendered, but you're relying on its value at the moment when view is being built. The latter is performed on an earlier lifecycle phase, so it is basically unavailable when you demand it.
Still, tag handler, or view build tag, like <c:forEach>, is the only way to populate the variable number of columns, as <p:column> is assessed when component tree is being built.
Another thing worth noting is that the backing bean bound to <c:forEach> tag's property, such as items, must be anything but view scoped, like request scoped, otherwise it will be recreated upon every request which will bring unexpected/undesired results, as the demanded bean is not there when you try to access its properties. There are some other setup constellations solving this issue, but they're not the subject of discussion here.
<p:dataTable value="#{customerBean.list}" var="map">
<c:forEach items="#{forEachBean.columnsMap}" var="entry">
<p:column headerText="#{entry.key}">
#{map[entry.key]}
</p:column>
</c:forEach>
</p:dataTable>
Also worth noting that there is a helper <p:columns> component that does roughly the same.
I want to process this form (valueChangueListener is not valid in real case).
This is the back bean:
public class TestBean extends PrivateBaseBean implements Serializable {
private List<String> strings;
#PostConstruct
public void init() {
strings = new ArrayList<String>();
strings.add("");
strings.add("");
strings.add("");
}
public void saveAction(ActionEvent event) {
StringBuilder textToShowInMessage = new StringBuilder();
for (String string : strings) {
textToShowInMessage.append(string);
textToShowInMessage.append("; ");
}
FacesMessage msg = new FacesMessage(super.getBundle().getString(
textToShowInMessage.toString()), "");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
getters... setters...
An the view:
....
<h:form>
<ui:repeat var="string" value="#{testBean.strings}">
<h:inputText value="#{string}" />
<br />
</ui:repeat>
<p:commandButton value="#{msg.save}"
actionListener="#{testBean.saveAction}" icon="ui-icon-disk"
update="#form" />
</h:form>
...
When the form is processed in the back bean string list always is blank.
How to process form intput's inside iteration, without any value changue listener?
There are some screenshots:
The same problem occurs with action or actionListener on
Your problem is not connected with PrimeFaces <p:commandButton>'s behaviour, but rather with a scoping problem that is implicilty created when using the <ui:repeat> tag.
First of all, let's depart from your example. Basically, you've got
<ui:repeat value="#{bean.strings}" var="s">
<h:inputText value="#{s}"/>
</ui:repeat>
with the backing List<String> strings.
The culprit is here: value="#{s}". The exported by <ui:repeat> variable s is visible only within its loop and it is not bound to any managed bean's property, but instead only to a local variable. Put it differently, s is not bound/equal to bean.strings[index] as one would expect and has no knowledge, as we see, where it originated from. So basically, you're off with a unilateral relationship: value from the bean is printed in your input properly, but the reverse is not happening.
The workarounds
Workaround #1: wrapper classes / model objects
The situation can be overcome by using a wrapper object for your class. In case of a string it could be a 'simple mutable string', like below:
public class MString {
private String string;//getter+setter+constructor
}
In this case the iteration will be working as predicted:
<ui:repeat value="#{bean.mstrings}" var="ms">
<h:inputText value="#{ms.string}"/>
</ui:repeat>
with the backing List<MString> mstrings.
Note that if you have your model class, like User, and will change its properties within <ui:repeat> the class itself will be effectively a wrapper, so that the properties will be set appropriately.
Workaround #2: chained property access
Another workaround consists of accessing an element of your collection directly from within a <h:inputText> tag. This way, any such property will be set by accessing the bean, then collection, then setting the property at the desired index. Excessively long, but that's how it is. As to the how question, <ui:repeat> provides for an exported current iteration status variable, varStatus, that will be used to access the array/collection in the managed bean.
In this case the iteration will also be working as predicted:
<ui:repeat value="#{bean.strings}" var="s" varStatus="status">
<h:inputText value="#{bean.strings[status.index]}"/>
</ui:repeat>
with the ordinary backing List<String> strings.
My workaround solution take the value directly from the page:
<ui:repeat id="repeat" value="#{bean.strings}" var="s" varStatus="status">
<h:inputText id="x" value="#{s.field}"/>
<h:commandLink style="margin: .5em 0" styleClass="commandLink" actionListener="#{bean.save(status.index)}" value="#{bundle.Send}"/>
</ui:repeat>
The save method:
public void save(String rowid) {
String jsParam = Util.getJsParam("repeat:" + rowid + ":x");
System.out.println("jsParam: " + jsParam); //persist...
}
The getJsParam method:
public static String getJsParam(String paramName) {
javax.faces.context.FacesContext jsf = javax.faces.context.FacesContext.getCurrentInstance();
Map<String, String> requestParameterMap = jsf.getExternalContext().getRequestParameterMap();
String paramValue = requestParameterMap.get(paramName);
if (paramValue != null) {
paramValue = paramValue.trim();
if (paramValue.length() == 0) {
paramValue = null;
}
}
return paramValue;
}
Can I bind my h:dataTable/rich:dataTable with some Map? I found that h:dataTable can work only with List object and deleting in List can be very heavy.
If you want to have only one method in your backing bean that provides the map, you can do something like this:
class Bean {
public Map<T,U> getMap() {
return yourMap;
}
}
and use this in your JSF view
<h:dataTable value="#{bean.map.keySet().toArray()}" var="key">
<h:outputText value="#{bean.map[key]}"/>
</h:dataTable>
This converts the key set into an array, which can be iterated by the datatable. Using the "()"-expression requires Unified Expression Language 2 (Java EE 6).
Yes, that's right. dataTable, ui:repeat and friends only work with Lists.
You can just add a managed bean method that puts map.keySet() or map.values() into a list depending on which you want to iterate over.
Typically when I want to iterate a map from a JSF view, I do something like
<h:dataTable value="#{bean.mapKeys}" var="key">
<h:outputText value="#{bean.map[key]}"/>
</h:dataTable>
with managed bean property
class Bean {
public List<T> mapKeys() {
return new ArrayList<T>(map.keySet());
}
}
or something like that.
Of course, this makes the most sense if you're using something like TreeMap or LinkedHashMap that preserves ordering.
I want to display java arraylist into JSF page. I generated arraylist from database. Now I want to display the list into JSF page by calling the list elements index by index number. Is it possible to pass a parameter to bean method from an EL expression in JSF page directly and display it?
You can access a list element by a specific index using the brace notation [].
#ManagedBean
#RequestScoped
public class Bean {
private List<String> list;
#PostConstruct
public void init() {
list = Arrays.asList("one", "two", "three");
}
public List<String> getList() {
return list;
}
}
#{bean.list[0]}
<br />
#{bean.list[1]}
<br />
#{bean.list[2]}
As to parameter passing, surely it's possible. EL 2.2 (or JBoss EL when you're still on EL 2.1) supports calling bean methods with arguments.
#{bean.doSomething(foo, bar)}
See also:
Our EL wiki page
Invoke direct methods or methods with arguments / variables / parameters in EL
I however wonder if it isn't easier to just use a component which iterates over all elements of the list, such as <ui:repeat> or <h:dataTable>, so that you don't need to know the size beforehand nor to get every individual item by index. E.g.
<ui:repeat value="#{bean.list}" var="item">
#{item}<br/>
</ui:repeat>
or
<h:dataTable value="#{bean.list}" var="item">
<h:column>#{item}</h:column>
</h:dataTable>
See also:
How iterate over List<T> and render each item in JSF Facelets
can some one help me with the following JSF dataTable? here I am getting data from database table and I used dataTable binding, but I don't know why it displays the rows 3 times in the screen, but if I remove the binding then it displays only one time.
<h:dataTable binding="#{threadController.dataTable}" var="category" value="#{threadController.queryCategories}" border="1" cellpadding="2" cellspacing="0">
<h:column>
<img src="../../images/directory.jpg" alt="Forum Icon" />
</h:column>
<h:column>
<h:form>
<h:commandLink value="#{category.cname}" action="#{threadController.categoryDateItem}" />
</h:form>
</h:column>
// defined globally
private HtmlDataTable dataTable;
private HtmlInputHidden dataItemId = new HtmlInputHidden();
public String categoryDateItem() {
category = (Category) dataTable.getRowData();
System.out.println("category action by select: "+category.getCname());
dataItemId.setValue(category.getId());
return "editItem"; // Navigation case.
}
#SuppressWarnings("unchecked")
public ArrayList<Category> getQueryCategories(){
return (ArrayList<Category>)HibernateUtil.getSession().createCriteria(Category.class).list();
}
output:
myText myText myText
The binding expression to bind this component to the bean value="#{threadController.queryCategories}".So value attribute is sufficient to retrieve data using dataTable tag.
Binding = component backing bean
Value= data model backing bean
So, you have the Value and Binding set correctly (at least, as far as I can see). Your problem may result from the fact that you're not caching the list you're getting back from the database in getQueryCategories().
You really can't have any idea how often getQueryCategories() will be called in the process of rendering that dataTable, so it's a good idea to do something like this:
// Somewhere near the top of the handler class.. create a cache variable:
private ArrayList<Category> qCategories = null;
// now for getQueryCategories
public ArrayList<Category> getQueryCategories(){
if ( qCategories == null ) { // qCategories should be a member of the handler
qCategories = (ArrayList<Category>)HibernateUtil.getSession().createCriteria(Category.class).list();
}
return qCategories
}
This kind of cache-ing is very helpful in JSF apps with handlers that are session of even request scoped, as again you can't really know how often JSF will evaluate your "value" expression in the dataTable.