UIData improvements
In JSF 2.3, <h:dataTable/> and <ui:repeat /> support Iterable and Map.

Iterable example

Let's create a simple demo to try it.
The backing bean.
1
@Model
2
public class IterableBean {
3
4
@Inject Logger LOG;
5
6
private Iterable data = Arrays.asList("javaee 8", "jsf 2.3");
7
8
public Iterable getData() {
9
LOG.log(Level.INFO, "called IterableBean.getData");
10
return data;
11
}
12
13
}
Copied!
And the facelets template.
1
<!DOCTYPE html>
2
<html lang="en"
3
xmlns="http://www.w3.org/1999/xhtml"
4
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
5
xmlns:f="http://xmlns.jcp.org/jsf/core"
6
xmlns:h="http://xmlns.jcp.org/jsf/html"
7
>
8
<f:view>
9
<h:head>
10
<title>JSF 2.3: DataModel Sample</title>
11
</h:head>
12
<h:body>
13
<h1>JSF 2.3: Iterable interface Sample</h1>
14
15
<h2> h:dataTable Iterable interface</h2>
16
<h:dataTable var="entry" value="#{iterableBean.data}">
17
<h:column>#{entry}</h:column>
18
</h:dataTable>
19
<h2> ui:repeat Iterable interface</h2>
20
<ui:repeat var="item" value="#{iterableBean.data}">
21
Item: #{item}
22
</ui:repeat>
23
</h:body>
24
</f:view>
25
</html>
Copied!

Map example

The backing bean.
1
@Model
2
public class MapBean {
3
4
@Inject
5
Logger LOG;
6
7
private Map<Integer, String> data = new HashMap<>();
8
9
public Map<Integer, String> getData() {
10
LOG.log(Level.INFO, "called MapBean.getData");
11
data.put(1, "java ee 8");
12
data.put(2, "jsf 2.3");
13
return data;
14
}
15
16
}
Copied!
And the facelets template.
1
<!DOCTYPE html>
2
<html lang="en"
3
xmlns="http://www.w3.org/1999/xhtml"
4
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
5
xmlns:f="http://xmlns.jcp.org/jsf/core"
6
xmlns:h="http://xmlns.jcp.org/jsf/html"
7
>
8
<f:view>
9
<h:head>
10
<title>JSF 2.3: DataModel Sample</title>
11
</h:head>
12
<h:body>
13
<h1>JSF 2.3:Map interface Sample</h1>
14
15
<h2> UIRepeat: Map interface</h2>
16
<ui:repeat var="entry" value="#{mapBean.data}">
17
Key: #{entry.key}
18
Value: #{entry.value}
19
</ui:repeat>
20
21
<h2>DataTable: Map Interface</h2>
22
<h:dataTable var="entry" value="#{mapBean.data}">
23
<h:column>#{entry.key}</h:column>
24
<h:column>#{entry.value}</h:column>
25
</h:dataTable>
26
</h:body>
27
</f:view>
28
</html>
Copied!

Iteration without backing data

In former versions, when you want to iterate a number collection from 0 to 10, you have to use c:forEach which from legacy JSP taglibs.
Now the new ui:repeat added this feature finally.
1
<ui:repeat begin="0" end="10" step="2" var="i">
2
#{i}<br />
3
</ui:repeat>
Copied!

Custom DataModel

JSF 2.3 provides a new @FacesDataModel to simplify the customization of your own DataModel.
The backing bean.
1
@Model
2
public class CustomBean {
3
4
@Inject
5
Logger LOG;
6
7
public UserData getUserData() {
8
LOG.log(Level.INFO, "called CustomBean.getUserData");
9
10
List<User> data = IntStream.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
11
.mapToObj(i -> new User("first " + i, "last " + i))
12
.collect(Collectors.toList());
13
14
return new UserData(data);
15
}
16
17
}
Copied!
UserData is just a POJO which wraps a list of User.
1
public class UserData {
2
List<User> users = new ArrayList<>();
3
4
public UserData() {
5
}
6
7
public UserData(List<User> users) {
8
this.users = users;
9
}
10
11
public List<User> getUsers() {
12
return users;
13
}
14
15
public void setUsers(List<User> users) {
16
this.users = users;
17
}
18
19
}
Copied!
1
public class User implements Serializable {
2
3
private String firstName;
4
private String lastName;
5
6
public User(String firstName, String lastName) {
7
this.firstName = firstName;
8
this.lastName = lastName;
9
}
10
11
public String getFirstName() {
12
return firstName;
13
}
14
15
public void setFirstName(String firstName) {
16
this.firstName = firstName;
17
}
18
19
public String getLastName() {
20
return lastName;
21
}
22
23
public void setLastName(String lastName) {
24
this.lastName = lastName;
25
}
26
27
@Override
28
public String toString() {
29
return "User{" + "firstName=" + firstName + ", lastName=" + lastName + '}';
30
}
31
32
}
Copied!
The facelets template.
1
<ui:repeat value="#{customBean.userData}" var="item">
2
User : #{item}<br />
3
</ui:repeat>
Copied!
We use UserData as UIRepeat backing data, it is not supported in JSF by default. Let's fill the gap via custom @FacesDataModel class.
1
@FacesDataModel(forClass = UserData.class)
2
public class UserDataModel extends DataModel<User> {
3
4
UserData data = null;
5
int index = 0;
6
7
public UserDataModel() {
8
this(null);
9
}
10
11
public UserDataModel(UserData data) {
12
this.data = data;
13
}
14
15
@Override
16
public boolean isRowAvailable() {
17
return this.index >= 0 && this.index < this.getRowCount();
18
}
19
20
@Override
21
public int getRowCount() {
22
return this.data != null ? this.data.getUsers().size() : 0;
23
}
24
25
@Override
26
public User getRowData() {
27
if (this.index >= 0 && this.index < this.getRowCount()) {
28
return this.data.getUsers().get(this.index);
29
}
30
31
return null;
32
}
33
34
@Override
35
public int getRowIndex() {
36
return this.index;
37
}
38
39
@Override
40
public void setRowIndex(int rowIndex) {
41
this.index = rowIndex;
42
}
43
44
@Override
45
public Object getWrappedData() {
46
return this.data;
47
}
48
49
@Override
50
public void setWrappedData(Object data) {
51
this.data = (UserData) data;
52
}
53
54
}
Copied!
Run this application on Glassfish, open your browser and navigate to http://localhost:8080/jsf-data-model/custom-datamodel.faces.
custom datamodel
Grab the source codes from my GitHub account, and have a try.
Last modified 8mo ago