How to Dynamically add a row in a table in JSF?
Solution 1:
All you need to do is basically indeed just adding an empty object to the datamodel behind the value
attribute of h:dataTable
.
But the same empty row needs to be preserved in the subsequent request as well. If the backing bean is request scoped, then the datamodel get reloaded without the empty row. This all should work when the bean is session scoped.
Further there are several errors in your JSF code. The h:dataTable
var
attribute is missing and the column content needs to be inside a h:column
.
<h:form>
<h:dataTable value="#{bean.list}" var="item">
<h:column><h:inputText value="#{item.value}" /></h:column>
</h:dataTable>
<h:commandButton value="Add" action="#{bean.add}"/>
</h:form>
A session or view scoped bean can look like this:
public class Bean {
private List<Item> list;
public Bean() {
list = new ArrayList<Item>();
}
public void add() {
list.add(new Item());
}
public List<Item> getList() {
return list;
}
}
The Item
class should of course have a default no-arg constructor. Normally this is already implicitly available, but if you define your own constructor with arguments, then it is not available anymore. You'll need to explicitly define it, otherwise you cannot do Item item = new Item();
anymore.
public class Item {
private String value;
public Item() {
// Keep default constructor alive.
}
public Item(String value) {
this.value = value;
}
// ...
}
If you prefer to keep the bean in the request scope, then you'll need to maintain the amount of newly added items, so that the bean can preserve the same amount on load.
public class Bean {
private List<Item> list;
private HtmlInputHidden count = new HtmlInputHidden();
public Bean() {
count.setValue(0);
}
public void add() {
list.add(new Item());
}
public List<Item> getList() {
if (list == null) loadList();
return list;
}
public HtmlInputHidden getCount() {
return count;
}
public void setCount(HtmlInputHidden count) {
this.count = count;
}
private void loadList() {
list = new ArrayList<Item>();
// Preserve list with newly added items.
for (int i = 0; i < (Integer) count.getValue(); i++) {
list.add(new Item());
}
}
}
You'll only need to add the following to the <h:form>
of the JSF page:
<h:inputHidden binding="#{bean.count}" converter="javax.faces.Integer" />
For more insights about using datatables in any way you may find this article useful: Using Datatables. It also contains a WAR file with lot of examples in both request and session scope.
Solution 2:
Take this table as an example:
<h:datatable value="#{myBean.list}" ...>
...
<h:column>
<h:commandButton value="Add a row" action="#{myBean.addRow}"/>
</h:column>
</h:datatable>
The method myBean.addRow will simply add a new element in your list:
public class MyBean {
private List<SomeClass> list;
...
public List<SomeClass> getList() {
return list;
}
public void addRow() {
list.add(new SomeClass());
}
}
When you will click on the button, the method addRow will add a new element in the list. The page will refresh and display the table with a new row.
Edit:
Regarding your post edition, three things:
Point 1: Could you please attach the stacktrace of your error?
Point 2: Your method addRow return a String
which is an ID used by JSF for the navigation. As this action does not involve any navigation (i.e. the user stay on the same page), simply return null
or ""
:
public String addRow() {
list.add(new Item("new data"));
return null;
}
Point 3: I suggest that your class Item
provide an empty constructor (in addition of your current constructor):
public Item() {
}