How to Dynamically add a row in a table in JSF? - jsf

In my application i need to add a row on a click of a button and this button will be in all the rows. Need help to do this?
Item Class
public class Item {
public Item()
{
}
private String value;
public Item(String value) { this.value = value; }
public void setValue(String value) { this.value = value; }
public String getValue() { return value; }
}
Manage Bean Class
public class MyMB
{
private List<Item> list;
public void addItem() { // JSF action method
list.add(new Item("Default"));
Iterator<Item> iterator = list.iterator();
while(iterator.hasNext())
{
Item item = (Item)iterator.next();
System.out.println(item.getValue());
}
System.out.println();
}
/**
* #return the list
*/
public List<Item> getList() {
if(list==null)
{
loadList();
}
return list;
}
private void loadList() {
list = new ArrayList<Item>();
list.add(new Item("Data"));
}
}
JSF code
<h:form>
<rich:dataTable value="#{myMB.list}" var="item" id="tabel">
<h:column><h:inputText value="#{item.value}" /></h:column>
<h:column><a4j:commandButton value="Add" actionListener="#{myMB.addItem}" reRender="tabel"/></h:column>

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.

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() {
}

Related

JSF ui repeat : Array index out of range: 0 [duplicate]

I would like to know if it possible to push a value from inside a <ui:repeat> to a map, a set or a list?
I would like to pass the value of the <h:inputtext> to a set.
Code:
<ui:repeat var="_par" value="#{cmsFilterParameterHandler.normaleSuchParameter()}">
<p:outputLabel value="#{_par.bezeichnung}" />
<p:spacer width="5px" />
<p:inputText id="me" value="#{??? push me to a set ???}"/>
<br /><br />
</ui:repeat>
With a Set, it is not possible as it doesn't allow referencing items by index or key. It's however possible with a List and a Map by just specifying the list index and map key in the input value.
With a List:
private List<String> list; // +getter (no setter necessary)
#PostConstruct
public void init() {
list = createAndFillItSomehow();
}
<ui:repeat value="#{bean.list}" varStatus="loop">
<h:inputText value="#{bean.list[loop.index]}" />
</ui:repeat>
With a Map (only if your environment supports EL 2.2 or JBoss EL):
private Map<String, String> map; // +getter (no setter necessary)
#PostConstruct
public void init() {
map = createAndFillItSomehow();
}
<ui:repeat value="#{bean.map.entrySet().toArray()}" var="entry">
<h:inputText value="#{bean.map[entry.key]}" />
</ui:repeat>
Noted should be that the canonical approach is to use a List of fullworthy javabeans. Let's assume a Javabean class named Par with properties id and value which maps exactly to a par table in DB with columns id and value:
private List<Par> pars; // +getter (no setter necessary)
#PostConstruct
public void init() {
pars = createAndFillItSomehow();
}
<ui:repeat value="#{bean.pars}" var="par">
<h:inputText value="#{par.value}" />
</ui:repeat>
Either way, it works as good when using <p:inputText>, it's in no way related to PrimeFaces, it's in the context of this question merely a jQuery based JSF UI component library. Just replace h: by p: to turn it on.
I'm not sure, if I understood your requirements correctly.
I suppose the following: You need a List of Strings in some backend and an ui:repeat tag to iterate over those strings with input-fields to edit them. Maybe there are some syntax-issues, but my idea should be clear:
public class Backend {
private List<String> myStrings;
public MyStringWrapper getMyStringWrapper(int index) {
return new MyStringWrapper(index);
}
public class MyStringWrapper {
private final int index;
public MyStringWrapper(int index) { this.index = index; }
public String getContent() { return myStrings.get(index); }
public void setContent(String newContent) { myStrings.add(index, newContent); }
}
}
In the frontend you use as follows:
<ui:repeat var="_index" value="#{backend.getIndexSequence()}">
<p:inputText value="#{backend.getMyStringWrapper(_index).content}"/>
</ui:repeat>
Of course, you have to provide a getIndexSequence-method which produces a list of ints ranging from 0 to the size of the strings.
Do you mean like this?
<p:inputText id="me" value="#{_par.input}"/>
in BackBean:
public class Par implements Serializable {
private String inputText;
private String bezeichnung;
public Par()
{
}
public void setInput(String input)
{
this.inputText = input;
}
public String getInput()
{
return this.inputText
}
public void setBezeichnung(String bezeichnung)
{
this.bezeichnung = bezeichnung;
}
public String getBezeichnung()
{
return this.bezeichnung
}
}

Pass changed value from inputText to bean

I'm trying to pass the changed value of the variable to a bean method:
<h:panelGroup rendered="#{! empty gradesBean.getAllGrades()}">
<h:dataTable value="#{gradesBean.getAllGrades()}" var="g">
<h:column>
<f:facet name="header">#{msg['outputSubject']}</f:facet>
<h:inputText value="#{g.subject}" onchange="#{g.subject}" />
</h:column>
<h:column>
<f:facet name="header">#{msg['outputGrade']}</f:facet>
<h:inputText value="#{g.mark}" onchange="#{g.mark}"/>
</h:column>
<h:column>
<h:form>
<h:commandButton value="#{msg['actionSave']}" action="#{gradesBean.edit(g)}" />
</h:form>
</h:column>
</h:dataTable>
(.........)
</h:panelGroup>
I want the changes of the user he does in the inputText on g.subject and g.mark to be passed to gradesBean.edit(g). When I try to do that somehow the same values of the original values of the both variables are passed. There are getter and setter methods in the respective object
#Named
#ViewScoped
public class GradesBean extends AbstractBean implements Serializable {
private static final long serialVersionUID = 320401008216711886L;
private static final String NO_GRADES_PRESENT = "keine Noten eingetragen";
private static final Logger loggerLogger.getLogger(GradesBean.class);
#Inject
private transient GradeDAO gradeDAO;
#Inject
private UserDAO userDAO;
private Grade grade;
private List<Grade> allGrades;
#PostConstruct
public void init() {
if (!isLoggedIn()) {
return;
}
grade = new Grade();
allGrades = getSession().getUser().getGrades();
}
public Grade getGrade() {
return grade;
}
public List<Grade> getAllGrades() {
return allGrades;
}
public String getGradeAverage() {
final List<BigDecimal> theDecimals = new ArrayList<>(allGrades.size());
for (final Grade g : allGrades) {
theDecimals.add(g.getMark());
}
final Configuration config = Configuration.getDefault();
final int scale = config.getScale();
final RoundingMode roundingMode = config.getRoundingMode();
try {
final BigDecimal average = de.unibremen.st.gradelog.businesslogic.Math
.average(theDecimals, scale, roundingMode);
return average.stripTrailingZeros().toPlainString();
} catch (final ArithmeticException e) {
logger.debug(
"Calculation of grade average has been called without any grades.", e);
return NO_GRADES_PRESENT;
}
}
public String getGradeMedian() {
final List<BigDecimal> theDecimals = new ArrayList<>(allGrades.size());
for (final Grade g : allGrades) {
theDecimals.add(g.getMark());
}
try {
final BigDecimal median = de.unibremen.st.gradelog.businesslogic.Math
.median(theDecimals);
return median.stripTrailingZeros().toPlainString();
} catch (final ArithmeticException e) {
logger.debug(
"Calculation of grades median has been called without any grades.", e);
return NO_GRADES_PRESENT;
}
}
public String save() {
if (!isLoggedIn()) {
return null;
}
final User user = getSession().getUser();
grade.setUser(user);
user.addGrade(grade);
gradeDAO.save(grade);
try {
userDAO.update(user);
} catch (final DuplicateUniqueFieldException e) {
throw new UnexpectedUniqueViolationException(e);
}
init();
return null;
}
public String edit() {
if (!isLoggedIn()) {
return null;
}
assertNotNull(grade);
final User user = getSession().getUser();
gradeDAO.update(grade);
try {
userDAO.update(user);
} catch (final DuplicateUniqueFieldException e) {
throw new UnexpectedUniqueViolationException(e);
}
init();
return null;
}
public String remove(final Grade theGrade) {
if (!isLoggedIn()) {
return null;
}
assertNotNull(theGrade);
final User user = getSession().getUser();
user.removeGrade(theGrade);
gradeDAO.remove(theGrade);
try {
userDAO.update(user);
} catch (final DuplicateUniqueFieldException e) {
throw new UnexpectedUniqueViolationException(e);
}
init();
return null;
}
}`
g.mark and g.subject will already call the corresponding setters on your backing bean class (I assume that public setters getMark() and getSubject() exist).
Since action="#{gradesBean.edit(g)}" is resolved at the server (when the page is first displayed), it will have the original values , not the changed values. Anyway , to see the changed values you can use ajax (f:ajax tag and the corresponding listener attribute should be set). But you don't have to do this. Simply change your handler to
action="#{gradesBean.edit()}" //no argument
and get the latest values from your bean class instance.
I could be more specific if you want.

Passing parameters in JSF and PrimeFaces

I am studying a PrimeFaces AutoComplete demo. I shortenied it from the full showcase demo. http://www.primefaces.org/showcase/ui/input/autoComplete.xhtml
AutoCompleteBean.java
#ManagedBean
public class AutoCompleteBean {
private Query query;
private List<Query> queries = new ArrayList<Query>();
#PostConstruct
public void init() {
queries.add(new Query(0, "Afterdark", "afterdark"));
queries.add(new Query(1, "Afternoon", "afternoon"));
queries.add(new Query(2, "Afterwork", "afterwork"));
queries.add(new Query(3, "Aristo", "aristo"));
}
public List<Query> completeQuery(String query) {
List<Query> filteredQueries = new ArrayList<Query>();
for (int i = 0; i < queries.size(); i++) {
Query skin = queries.get(i);
if(skin.getName().toLowerCase().contains(query)) {
filteredQueries.add(skin);
}
}
return filteredQueries;
}
public void onItemSelect(SelectEvent event) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Item Selected", event.getObject().toString()));
}
public Query getQuery() {
return query;
}
public void setQuery(Query query) {
this.query = query;
}
}
Query.java
public class Query {
private int id;
private String displayName;
private String name;
public Query() {}
public Query(int id, String displayName, String name) {
this.id = id;
this.displayName = displayName;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return name;
}
}
I omitted a Convert class, which I think is not that relevant.
search.xhtml
<h:form>
<p:growl id="msgs" showDetail="true" />
<h:panelGrid columns="2" cellpadding="5">
<p:autoComplete id="queryPojo" value="#{autoCompleteView.query}"
completeMethod="#{autoCompleteView.completeQuery}" var="query"
itemLabel="#{query.displayName}" itemValue="#{query}"
converter="queryConverter" forceSelection="true" />
<p:commandButton value="search" oncomplete="PF('dlg').show()"/>
</h:panelGrid>
</h:form>
I have three questions for this:
1) completeMethod="#{autoCompleteView.completeQuery}": completeQuery method is called without passing a parameter, but it's defined as completeQuery(String query). How does this work?
2) value="#{autoCompleteView.query}". Query is an object defined in AutoCompleteBean. How can this Query object take user input string as its value? Usually InputText's value is good for taking user's input, which is a String value.
3) Can I still add an attribute "action=..." to the p:autoComplete componenet?
The converter class that you omitted here plays the real game.... Lets see your questions
As you see converter class overrides 2 methods
getAsString and getAsObject
1)the value
completeMethod="#{autoCompleteView.completeQuery}
gets refactred to
autoCompleteView.completeQuery(autoCompleteView.query);
as you can find to string method in Query class.
2).as converter is defined for autocomplete it calls getAsString method to render on screen. when selected getAsObject method is called to convert string value to object(Query).
3)you can use ajax select event
<p:ajax event="select" listener="#{autoCompleteView.someMethod}">
or call a remoteCommand by onSelect attribute in p:autoComplete
<p:autoComplete id="queryPojo" value="#{autoCompleteView.query}" onSelect="someRemoteCommand();"
completeMethod="#{autoCompleteView.completeQuery}" var="query"
itemLabel="#{query.displayName}" itemValue="#{query}"
converter="queryConverter" forceSelection="true" />
<p:remoteCommand name="someRemoteCommand" update="queryPojo" actionListener="#{autoCompleteView.execute}" />

Primefaces p:orderList java backing list does not update

I am currently implementing a orderable list using PrimeFaces' component, embedded inside a . I was able to get the list to appear properly with my items. However, when I saved the list and submitted it back to the server, the rearranged items did not get reflected in the backing bean for some reason. Since the Primefaces showcase was able to see the changes, what am I doing wrong?
XHTML Snippet:
<h:form id="confirmDialogForm">
<p:confirmDialog id="arrangeProjDialog" widgetVar="arrangeDlg" width="600"
header="Meeting Order"
appendToBody="true" message="Drag and drop to rearrange meeting order">
<p:orderList id="arrangeProjDialogList"
value="#{adminMeetingListBean.orderProjList}"
converter="#{adminMeetingListBean.rowConverter}"
var="po"
controlsLocation="left"
styleClass="wideList"
itemLabel="#{po.projectTitle}"
itemValue="#{po}"
>
<f:facet name="caption">Proposals</f:facet>
</p:orderList>
<p:commandButton value="Save" ajax="true" process="arrangeProjDialogList #this"
actionListener="#{adminMeetingListBean.updateProposalMeetingOrder}" onclick="arrangeDlg.hide();">
</p:commandButton>
<p:button value="Cancel" onclick="arrangeDlg.hide(); return false;" />
</p:confirmDialog>
</h:form>
Backing Bean:
public void updateProposalMeetingOrder() {
if (selectedMeeting != null) {
orderProjTitles.get(0);
meetingService.updateMeetingProjSequence(orderProjList, selectedMeeting.getMeetingId());
}
}
The List is a list of POJO "ProposalOrderRow" objects. This has the definition:
public class ProposalOrderRow implements Serializable {
private static final long serialVersionUID = -5012155654584965160L;
private int dispSeq;
private int appId;
private int assignmentId;
private String refNo;
private String projectTitle;
public int getDispSeq() {
return dispSeq;
}
public void setDispSeq(int dispSeq) {
this.dispSeq = dispSeq;
}
public int getAppId() {
return appId;
}
public void setAppId(int appId) {
this.appId = appId;
}
public String getRefNo() {
return refNo;
}
public void setRefNo(String refNo) {
this.refNo = refNo;
}
public String getProjectTitle() {
return projectTitle;
}
public void setProjectTitle(String projectTitle) {
this.projectTitle = projectTitle;
}
public int getAssignmentId() {
return assignmentId;
}
public void setAssignmentId(int assignmentId) {
this.assignmentId = assignmentId;
}
}
Converter:
#FacesConverter("proposalOrderRowConverter")
public class ProposalOrderRowConverter implements Converter {
private List<ProposalOrderRow> orderRows;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String newValue) {
if (newValue.isEmpty()) {
return null;
}
for (ProposalOrderRow item : orderRows) {
String refNo = item.getRefNo();
if (refNo.equals(newValue)) {
return item;
}
}
return null;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return "";
}
ProposalOrderRow row = (ProposalOrderRow) value;
String output = row.getRefNo();
return output;
}
public List<ProposalOrderRow> getOrderRows() {
return orderRows;
}
public void setOrderRows(List<ProposalOrderRow> orderRows) {
this.orderRows = orderRows;
}
}
This problem is caused by appendToBody="true" in the confirm dialog. Setting it to false solved the problem.
See link here: link

Is SelectableDataModel mandatory on <p:dataTable selection>; how do I implement it

Is it mandatory create a class that implements SelectableDataModel when I want to use selection property on my <p:dataTable>? If yes, how can I "link" my dataTable with a class that implements SelectableDataModel?
No, that's not necessary. You can just specify the rowKey attribute so that <p:dataTable> can figure the unique identifier of the row without the need for SelectableDataModel.
<p:dataTable value="#{bean.items}" var="item" rowKey="#{item.id}" ...>
For the case you're interested, or need to, here's how you should implement it:
#ManagedBean
#ViewScoped
public class Bean implements Serializable {
private List<Item> items;
private ItemDataModel itemModel;
public Bean() {
items = itemService.list();
itemModel = new ItemDataModel(items);
}
// ...
}
Where the ItemDataModel look like this:
public class ItemDataModel extends ListDataModel<Item> implements SelectableDataModel<Item> {
public ItemDataModel() {
// Default c'tor, keep alive.
}
public ItemDataModel(List<Item> data) {
super(data);
}
#Override
public Item getRowData(String rowKey) {
List<Item> items = (List<Item>) getWrappedData();
for (Item item : items) {
if (item.getId().equals(rowKey)) {
return item;
}
}
return null;
}
#Override
public Object getRowKey(Item item) {
return item.getId();
}
}
Finally use itemModel instead of items as <p:dataTable value>.
<p:dataTable value="#{bean.itemModel}" var="item" ... />

Resources