Datatable with rows of inputs - jsf

I have a datatable that generates rows of text inputs and selection lists and when I submit the values are null. I realize this is because jsf is generating separate random ID's for each input. I really need the data by rows and then parse out what I need. Any suggestions?:
<h:dataTable id="returnableItems" value="#{returnableItemsBean.orderCustomerFamilyItems}" var="item" styleClass="data stripeTable center">
<h:column>
<f:facet name="header">Item Number</f:facet>
<h:outputText id="itemNum" value="#{item.itemNum}"/>
</h:column>
<h:column>
<f:facet name="header">Description</f:facet>
<h:outputText value="#{item.itemDescription}"/>
</h:column>
<h:column>
<f:facet name="header">Original Quantity</f:facet>
<h:outputText value="#{item.originalQuantity}"/>
</h:column>
<h:column>
<f:facet name="header">Remaining Quantity</f:facet>
<h:outputText value="#{item.eligibleQuantity}"/>
</h:column>
<h:column>
<f:facet name="header">Quantity For Return</f:facet>
<h:panelGrid styleClass="stepper">
<span class="ns ui-stepper">
<input id="returnQuantity" type="text" name="returnQuantity" size="2" autocomplete="off" class="ui-stepper-textbox" value="0" />
<button type="button" name="ns_button_1_0" value="" class="ui-stepper-plus" onclick="setMaxVal(this,#{item.eligibleQuantity});">+</button>
<button type="button" name="ns_button_2_0" value="" class="ui-stepper-minus">-</button>
</span>
</h:panelGrid>
</h:column>
<h:column>
<f:facet name="header">Reason for Return</f:facet>
<h:selectOneMenu id="returnReason" value="#{returnableItemsBean.orderReason.ordReasonCode}">
<f:selectItems value="#{returnableItemsBean.orderReasons}"/>
<f:converter converterId="orderReasonConverter"/>
</h:selectOneMenu>
</h:column>
<h:column>
<f:facet name="header">Return/Replacement</f:facet>
<h:selectOneMenu id="returnOption">
<f:selectItem itemLabel="Option" itemValue=""/>
<f:selectItem itemLabel="Return" itemValue="return"/>
<f:selectItem itemLabel="Replacement" itemValue="replacement"/>
</h:selectOneMenu>
</h:column>
</h:dataTable>

I fail to understand why you use HTML input and buttons and not JSF ones in your form. Use <h:inputText> and <h:commandButton> instead, and bind their values to a property in your backing bean. The whole table should be inside a <h:form> ... </h:form> tag, of course.
Also, your input should probably be bound to a property of the "item" variable, like you do for output. If not, each row will have an input bound to the same property of your backing bean.
Are you using JSP, or Facelets ?

jsf is generating separate random ID's for each input
The IDs are not random; h:dataTable is a NamingContainer and manages its childrens clientIds so they become unique (a requirement of the HTML spec). JSF: working with component IDs (id vs clientId).
The straight HTML you have added will be difficult to integrate with the JSF model-view-presenter framework. It would be better to stick to JSF controls. This code shows how you can manipulate client-side field values using JavaScript and a custom TLD function:
<h:dataTable value="#{rowDataBean.data}" var="row">
<h:column>
<f:facet name="header">
<h:outputText value="product" />
</f:facet>
<h:outputText value="#{row.name}" />
</h:column>
<h:column id="column2">
<f:facet name="header">
<h:outputText value="quantity" />
</f:facet>
<h:inputText id="quantityCount" value="#{row.quantity}" />
<h:commandButton value="+"
onclick="increment('#{id:clientId('quantityCount')}'); return false;" />
</h:column>
</h:dataTable>
The invoked JavaScript:
function increment(id) {
//TODO: error handling
var textField = document.getElementById(id);
var value = textField.value;
value++;
textField.value = value;
}
If you are using Facelets instead of JSPs, you will need to define the function in a Facelets tag library.

Related

Using h:selectBooleanCheckbox to delete multiple rows in JSF datatable

I have followed the instruction as per the posts How to select multiple rows of <h:dataTable> with <h:selectBooleanCheckbox> and Using <h:selectBooleanCheckbox> with Map in a paginated <p:dataTable> throws NullPointerException
on how to delete multiple rows from a datatable. However, the checked Map object is not being populated with the the selected values. When I debug the application it is always empty.
Here is my code:
applicantSearch.xhtml
<ui:composition template="/WEB-INF/templates/main.xhtml">
<ui:define name="menu">
<ui:include src="/protected/views/menu.xhtml"/>
</ui:define>
<ui:define name="content">
<h:outputScript target="body">
formatMyTable();
</h:outputScript>
<h3>Search for Applicants</h3>
<h:form id="searchForm" class="form-search">
<h:panelGroup id="results" class="table-responsive">
<h:outputText id="informationMessage"
value="#{applicantSearchBacking.infoMessage}"
rendered="#{applicantSearchBacking.infoMessage ne null}"
class="informationMessage"/>
<div class="btn-toolbar">
<h:commandButton value="Delete Selected" onclick="if (! confirm('Are you sure you want to delete the selected applicants?')) return false" action="#{applicantSearchBacking.removeSelectedApplicants}" class="btn btn-danger btn-large pull-right">
<f:ajax render=":searchForm:results :searchForm:messages" onevent="formatMyTable"
/>
</h:commandButton>
</div>
<br/>
<h:dataTable value="#{applicantSearchBacking.applicantList}"
var="currentApplicant" styleClass="table table-hover table-condensed table-bordered"
>
<h:column>
<f:facet name="header">
Applicant ID
</f:facet>
#{currentApplicant.applicantId}
</h:column>
<h:column>
<f:facet name="header">
First Name
</f:facet>
#{currentApplicant.firstName}
</h:column>
<h:column>
<f:facet name="header">
Middle Name
</f:facet>
#{currentApplicant.middleName}
</h:column>
<h:column>
<f:facet name="header">
Last Name
</f:facet>
#{currentApplicant.lastName}
</h:column>
<h:column>
<f:facet name="header">
Actions
</f:facet>
<!-- User Access control section <c:if test=""> -->
<h:link value="Edit" outcome="#{applicantSearchBacking.editApplicant()}" class="btn btn-primary btn-sm">
<f:param name="myApplicantId" value="#{currentApplicant.applicantId}">
</f:param>
</h:link>
<!-- </c:if> -->
<!-- <c:if test=""> -->
<h:selectBooleanCheckbox id="del_checkbox" value="#{applicantSearchBacking.checked[currentApplicant.applicantId]}">
</h:selectBooleanCheckbox>
<!-- </c:if> -->
</h:column>
</h:dataTable>
</h:panelGroup>
<h:messages id="messages" class="errorMessage"/>
</h:form>
</ui:define>
applicantSearchBacking.java
private Map<Integer, Boolean> checked = new HashMap<>();
public void removeSelectedApplicants()
{
try {
List<Applicant> applicantsToDelete = new ArrayList<>();
for (Applicant applicant : applicantList) {
Boolean itemChecked = checked.get((applicant.getApplicantId()));
if (itemChecked !=null && itemChecked) {
applicantsToDelete.add(applicant);
}
}
applicantManager.removeSelectedApplicants(applicantsToDelete);
if (!checked.isEmpty())
infoMessage = "Applicant(s) deleted successfully";
checked.clear();
}
This construct will only work if exactly the same data model is preserved for the postback. So, if your bean is #RequestScoped, then it must be creating exactly the same applicantList inside #PostConstruct as it was when the form was displayed. Otherwise, you need to make the bean #ViewScoped. In any case, I assume that the getter method of applicantList doesn't do any business logic, but just solely returns the property.
See also:
Why JSF calls getters multiple times
How to choose the right bean scope?
commandButton/commandLink/ajax action/listener method not invoked or input value not updated - point 4
As per your question update, there's another potential mistake which may cause this construct to fail:
<f:ajax render=":searchForm:results :searchForm:messages" onevent="formatMyTable" />
The execute of <f:ajax> is here unspecified and uses then thus the default of #this. You need to explicitly specifiy #form in order to process the entire form on submit, otherwise only the parent component (the command button itself) would be processed.
<f:ajax execute="#form" ... />

How can i transfer a column value to my managed bean?

I am using a datatable in JSF:
<h:dataTable var="dataItem" value="#{operationsBean.creneauxMedecin}" border="1">
<f:facet name="header">
<h:outputText value="Rendez-vous de #{operationsBean.medecin.titre} #{operationsBean.medecin.prenom} #{operationsBean.medecin.nom} le #{operationsBean.txtJour}"></h:outputText>
</f:facet>
<h:column>
<f:facet name="header">
<h:outputText value="Créneau horaire"></h:outputText>
</f:facet>
<h:outputText id="id" value="#{dataItem.hdebut}h#{dataItem.mdebut}-#{dataItem.hfin}h#{dataItem.mfin}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Client"/>
</f:facet>
<h:outputText value="#{operationsBean.clt }"/>
</h:column>
</h:dataTable>
Please how can i transfer a column's(((dataItem.id))) value to my managed bean ?
N.B dataItem is a datatable row that contains(id,version,hdebut,mdebut....)
You have to implement an action listener method in your managed bean and call that via a command link and pass a param as part of the tag, if you google you can see many examples.
<h:commandLink value="click me" actionListener="#{managedbean.actionListenerMethod}">
<f:param name="paramInternalId" value="#{dataTableIter.id}" />
</h:commandLink>
http://docs.oracle.com/javaee/5/tutorial/doc/bnaqd.html#bnaqj

navigating to an anchor when editing a row in datable with jsf

(Using jsf-2)
I have a dataTable with one column which can be edited:
<h:column>
<f:facet name="header">
<h:outputText value="Label" style="font-weight: bold">
</h:outputText>
</f:facet>
<h:inputText value="#{m.author2displayed}" rendered="#{m.editable}" size="10"/>
<h:outputText value="#{m.author2displayed}" rendered="#{not m.editable}"/>
<h:commandButton value="save edits" rendered="#{m.editable}" onclick="submit()" action ="#{finalCheckBean.saveedits()}"/>
</h:column>
When I click on "save edits", we stay on the same view and that's the desired effect (finalCheckBean.saveedits() returns null).
Problem: the browser scrolls all back to the top of the page, whereas I'd like that the page stays displayed at the level of the row just edited. How can I achieve that?
If you're already using JSF2, just bring in some ajax magic using <f:ajax> in the command button. Wrap the to-be-executed and rendered components in a common component and reference it in execute and render of <f:ajax>.
<h:column>
<f:facet name="header">
<h:outputText value="Label" style="font-weight: bold" />
</f:facet>
<h:panelGroup id="author2displayed">
<h:inputText value="#{m.author2displayed}" rendered="#{m.editable}" size="10"/>
<h:outputText value="#{m.author2displayed}" rendered="#{not m.editable}"/>
<h:commandButton value="save edits" rendered="#{m.editable}" onclick="submit()" action="#{finalCheckBean.saveedits()}">
<f:ajax execute="author2displayed" render="author2displayed" />
</h:commandButton>
</h:panelGroup>
</h:column>
This way the scroll position will remain unchanged (provided that you still return null or void from the action method).

navigation when bean returns objects

Beginner question concerning changing the information in a view (and not going to a different .xhtml page). My .xhtml displays a datatable (which shows a list of people), but I want the user to be able to narrow the amount of info displayed by typing in the start of someones name into an inputtext and then clicking the commandbutton. Both the commandbutton and the datatable execute the same bean method, but if the inputtext has data the sql uses Like (this uses jdbc). My form is below, the problem is that I get an "Unable to find matching navigation case with from-view-id" because my managed bean returns a list of objects, not "success" or "failure". Also, the whole form seems inefficient. This seems like a fairly common scenario - calling managed bean's method that returns objects and not navigation instructions. How do I get rid of the error messages? Is there a better way to do this?
Here is code:
<h:head>
<h:outputStylesheet library="css" name="table-style.css" />
</h:head>
<h:body>
<h1>MavenWeb</h1>
<h:form>
<h:panelGrid columns="3">
Select a customer:
<h:inputText id="listName" value="#{customer.listName}"
size="20" >
</h:inputText>
<h:commandButton value="Submit"
action="#customer.getCustomerList()}" />
</h:panelGrid>
<h:dataTable value="#{customer.getCustomerList()}" var="c"
styleClass="order-table"
headerClass="order-table-header"
rowClasses="order-table-odd-row,order-table-even-row"
>
<h:column>
<f:facet name="header">
Customer ID
</f:facet>
#{c.customerID}
</h:column>
<h:column>
<f:facet name="header">
Name
</f:facet>
#{c.name}
</h:column>
<h:column>
<f:facet name="header">
Address
</f:facet>
#{c.address}
</h:column>
<h:column>
<f:facet name="header">
Created Date
</f:facet>
#{c.created_date}
</h:column>
</h:dataTable>
</h:form>
</h:body>
First, make sure your managed bean has ViewScope, so only the form values will be affected on every request while you're in the same view. Second, you can add ajax behavior to your <h:commandButton> and render the <h:datatable> with the new values. Third, never put business logic inside your attribute getters, because the JSF framework could make 2 or more calls of those getters (depending on your design).
Using these rules, you can remake your code like this:
#ViewScoped
#ManagedBean
public class Customer {
private String listName;
private List<CustomerDTO> customerList;
public Customer() {
listName = "";
customerList = null; //maybe you can initialize your list here
}
//getters and setters...
//look that this method returns a void (no need to return a navigation rule)
public void fillCustomerList() {
//your method/way to fill the customerList goes here...
//I'll just put a code example
customerList = new ArrayList<CustomerDTO>();
customerList.add(new CustomerDTO(1, "Luiggi Mendoza", "Lima", new Date());
customerList.add(new CustomerDTO(2, "StackOverflow", "Web", new Date());
}
}
The code fragment for your page
<h:form>
<h:panelGrid columns="3">
Select a customer:
<h:inputText id="listName" value="#{customer.listName}" size="20" />
<h:commandButton value="Submit" action="#customer.fillCustomerList}">
<f:ajax execute="#this" render="dtMyDataTable" />
</h:commandButton>
</h:panelGrid>
<h:dataTable id="dtMyDataTable" value="#{customer.customerList}" var="c"
styleClass="order-table"
headerClass="order-table-header"
rowClasses="order-table-odd-row,order-table-even-row">
<h:column>
<f:facet name="header">
Customer ID
</f:facet>
#{c.customerID}
</h:column>
<h:column>
<f:facet name="header">
Name
</f:facet>
#{c.name}
</h:column>
<h:column>
<f:facet name="header">
Address
</f:facet>
#{c.address}
</h:column>
<h:column>
<f:facet name="header">
Created Date
</f:facet>
#{c.created_date}
</h:column>
</h:dataTable>
</h:form>
More info on this matter:
Does view scope bean survive Navigation JSF
Learning JSF2: Ajax in JSF – using f:ajax tag

Exception thrown while accessing HTML DataTable in backing bean

I am building a simple application in JSF with the CrUD functionality. I am trying to implement edit functionality using the tomahawk component .I am unable to retrieve the selected row in my backing bean.
Here's my JSP file snip:
<t:dataTable id="data"
binding="#{selectOneRowList.dataTable}"
styleClass="scrollerTable"
headerClass="standardTable_Header"
footerClass="standardTable_Header"
rowClasses="standardTable_Row1,standardTable_Row2"
columnClasses="standardTable_Column,standardTable_ColumnCentered,standardTable_Column"
var="car"
value="#{selectOneRowList.list}"
sortColumn="#{selectOneRowList.sortColumn}"
sortAscending="#{selectOneRowList.sortAscending}"
preserveDataModel="false"
preserveSort="true"
preserveRowStates="true"
rows="10"
>
<h:column>
<f:facet name="header">
<h:outputText value="Select"/>
</f:facet>
<t:selectOneRow groupName="selection" id="hugo" value="#{selectOneRowList.selectedRowIndex}"
onchange="submit();" immediate="true"
valueChangeListener="#{selectOneRowList.processRowSelection}"/>
</h:column>
<h:column>
<f:facet name="header">
</f:facet>
<h:outputText value="#{car.id}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Cars" />
</f:facet>
<h:outputText value="#{car.type}" />
</h:column>
<t:column sortable="true">
<f:facet name="header">
<h:outputText value="Color" />
</f:facet>
<h:outputText value="#{car.color}" />
</t:column>
</t:dataTable>
Here's my backing bean SelectOneRowList.java :
public void editCar(ActionEvent event) {
System.out.println("Row number ## " + _selectedRowIndex.toString() + " selected!");
System.out.println("Datatable ::"+ dataTable);
System.out.println("Row Count ::" + dataTable.getRowCount());
dataItem = (SimpleCar) getDataTable().getRowData();
//selectedCar = (SimpleCar) _list.get(Integer.parseInt(_selectedRowIndex.toString()));
selectedCar = (SimpleCar) dataTable.getRowData();
System.out.println(dataTable.getRowData());
}
My DTO{Data Transfer Object} which is SimpleCar.java contains the variables ID, type, color and their respective setters/getters.
The dataItem variable is of type "SimpleCar". The dataTable is of type HTMLDataTable. I am able to get the the first 3 SOP's but the 4th SOP isn't printed. I receive the following exception on the server :
javax.faces.el.EvaluationException: Exception while invoking expression #{selectOneRowList.editCar}
org.apache.myfaces.el.MethodBindingImpl.invoke(MethodBindingImpl.java:156)
javax.faces.component.UICommand.broadcast(UICommand.java:89)
javax.faces.component.UIViewRoot._broadcastForPhase(UIViewRoot.java:97)
javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:171)
org.apache.myfaces.lifecycle.InvokeApplicationExecutor.execute(InvokeApplicationExecutor.java:32)
org.apache.myfaces.lifecycle.LifecycleImpl.executePhase(LifecycleImpl.java:95)
org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:70)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:139)
org.apache.myfaces.webapp.filter.ExtensionsFilter.doFilter(ExtensionsFilter.java:341)
On click of the edit button the editCar method in my backing bean is invoked. I need to get the data of the selected row in my backing bean. Why is the exception occurring ?
The above example is taken from the tomawhawk examples WAR distributed on the website. I have gone through many links including the ones on BalusC but none of them have helped.
Regards,
To use DataTable.getRowData() you would have to put the button tied to editCar() within the data table. So, on your JSP would need to add another column, such as:
...
<t:column sortable="true">
<f:facet name="header">
<h:outputText value="Color" />
</f:facet>
<h:outputText value="#{car.color}" />
</t:column>
<t:column sortable="false">
<h:commandButton value="Edit" action="#{selectOneRowList.editCar}" />
</t:column>
</t:dataTable>

Resources