Values in rich:dataTable not getting updated in the bean - jsf

This problem is when we are migrating to richfaces4.3 and JSF2.0.
The context is: A rich:dataTable takes answerId, answerDefinition etc in their respective columns. An onchange event is executed on the answerid column, wherein a new row is added.
<rich:extendedDataTable rowKeyVar="currentRowVar"
value="#{DiscQuestMgtController.answerChoiceList}"
var="dataItem" rowClasses="row1, row2" id="ansChoiceList">
<rich:column width="160" sortable="false">
<h:inputText id="answerID" value="#{dataItem.answerId}"
onchange="addNewRowToAnsChoice('DiscQuestMgtForm',#{fn:length(DiscQuestMgtController.answerChoiceList)});" />
</rich:column>
<rich:column sortable="false">
<h:inputText id="choiceValue" value="#{dataItem.choiceValue}"/>
</rich:column>
<rich:column sortable="false">
<h:inputText id="definitionId" value="#{dataItem.definition}"/>
</rich:column>
</rich:extendedDataTable>
When retrieving the values into the dataTable, the working is fine. But when I enter the value into answerId(the first cell) and shift focus, the existing row is totally eliminated creating a new row.
addNewRowToAnsChoice , JS method, does few validations of the entered values and in turn calls a a4j function .. addEmptyAnsChoice .. The below is the code, that adds a new row, in the backing bean.
//AddNewRow
public void addEmptyRow( ActionEvent ae )
{
removeEmptyChoiceRow();
String logStr = " DiscQuestMgtController:addEmptyRow()>>>";
logger.debug( logStr + " Entered" );
AnswerChoiceBean bean = new AnswerChoiceBean();
bean.setQuestionId( discQuestBean.getQuestionId() );
logger.debug( logStr + "Choice list size: answerChoiceList size-->",answerChoiceList.size() ); //#abc
if ( answerChoiceList.size() == 1 )
answerChoiceList.get( 0 ).setQuestionId( discQuestBean.getQuestionId() );
if ( !answerChoiceList.contains( bean ) )
answerChoiceList.add( bean );
logger.debug( logStr + " Returned" );
}
removeEmptyChoiceRow() checks if there are any existing empty rows and deletes them, to add a new row. #abc, the list size is always the default one. (there is always one row on page load), which is just not changing though the values are entered. Pls suggest!!
This is the a4j:function:
<a4j:jsFunction name="addEmptyAnsChoice"
render="ansChoiceList,addDiscQuest,applyDiscQuest"
actionListener="#{DiscQuestMgtController.addEmptyRow}"
oncomplete="focusToNewCell('DiscQuestMgtForm',#{fn:length(DiscQuestMgtController.answerChoiceList)});">
</a4j:jsFunction>

A major change from JSF 1.2 to JSF 2 is the introduction of the attributes render and execute replacing rerender.
execute partly updates the model of the marked part of the tree
render renders the marked part of the tree and sends it via AJAX for replacing the original part.
This is handy, when e.g. a Listener updates values also being shown as input-fields on the page.
Update: You are calling addNewRowToAnsChoice on change of answerId, calling the a4j:jsFunction addEmptyAnsChoice later on.
There the elements ansChoiceList ,addDiscQuest,applyDiscQuest get rerendered, but nothing is being sent for execute. So the line you entered the answerId in - well - is never updated on server side.
In the next step, in the actionListener you remove all empty lines (so also this one, too) and add a fresh line on the bottom.
IMO, try skipping the jsFunction call and instead letting the same stuff being executed as a f:ajax or a4j:ajax. The (untested) code might look like following...
<h:inputText id="answerID" value="#{dataItem.answerId}"
onchange="<yourValidationFunction>">
<a4j:ajax execute="#this"
render="ansChoiceList addDiscQuest applyDiscQuest"
actionListener="#{DiscQuestMgtController.addEmptyRow}"
oncomplete="focusToNewCell('DiscQuestMgtForm',#{fn:length(DiscQuestMgtController.answerChoiceList)});">
</a4j:ajax>
</h:inputText>
This way you keep the ID from the value to be updated on server side.
Hope it works and helps...

Related

Datatable selection - inputTextArea doesn't have up-to-date contents

Use case
I have a dialog that contains a datatable and an inputTextArea. The datatable is filled when the user clicks a button. When some (single) row of the datatable is selected, I want the contents of this row to be inserted at the end of the inputTextArea.
Problem
When using the selection attribute of the datatable, I dont have the most recent contents in the inputTextArea. Scenario: I enter "ABC" in my inputTextArea, then click the button, then make my inputTextArea "FFF" and select a row ("dtText") from the datatable. The new contents of the inputTextArea is "ABC dtText" instead of "FFF dtText".
What I tried
I tried to add a new listener but I cannot figure out how to bring it in properly so that it is being called before the selection happens. Found nothing on google or in the PF User Guide.
I was hoping the blur event updates my ruleText, but it doesn't happen.
inputTextArea
<p:inputTextarea value="#{mrBean.selectedElement.ruleTxt}" id="rt1New" rows="20" cols="100" autoResize="false">
<f:ajax event="blur" update="duoDlgForm2" />
</p:inputTextarea>
datatable
<p:dataTable id="qPdt" var="p" value="#{regelBean.queriedParams}" rowKey="#{p}"
selection="#{mrBean.selectedParam}" selectionMode="single">
<p:ajax event="rowSelect" update="#form"/>
<p:column ...
java
public void setSelectedParam(ParamOrDBParamModel selectedParam) {
if(selectedParam != null) {
selectedElement.setRuleTxt(selectedElement.getRuleTxt() + "\n" + selectedParam.getName().trim());
How can I work with the most recent text (selectedElement.getRuleTxt) when I am inside the setSelectedParam method?
PF 4.0
EDIT: if I try jquery, what do I have to insert into my function
$('#qPdt tr').on('click', function() {
// I reach this point, now what? How to get the contents of a row when I know the type?
$("#rt1New").append( ?? )
});

p:selectOneMenu get just first value from list primefaces JSF

i have p:selectOneMenu, all values are viewed correctly but just first on my list can be chosen for example, on my list i have e-mail addresses, i can choose everyone but mail is sending just on first of them on list. My JSF code:
<p:dataTable value="#{additionalOrdersBean.additionalOrdersList}"
var="additionalOrders" rowIndexVar="lp" id="myTable" editable="true>
<p:column>
<p:selectOneMenu id="recipient" value="#{additionalOrdersBean.mailTo}" converter="#{mailConverter}" required="true" requiredMessage="#{loc['fieldRequired']}">
<f:selectItems value="#{buildingBean.buildingList2}" var="mail" itemLabel="#{mail.mail1}" itemValue="#{mail.mail1}" />
<f:selectItems value="#{buildingBean.buildingList2}" var="mail" itemLabel="#{mail.mail2}" itemValue="#{mail.mail2}" />
<f:selectItems value="#{buildingBean.buildingList2}" var="mail" itemLabel="#{mail.mail3}" itemValue="#{mail.mail3}" />
</p:selectOneMenu>
<h:message for="recipient" style="color:red"/>
<h:commandButton value="#{loc['send']}" action="#{additionalOrdersBean.sendProtocol()}" onclick="sendProtocolDialog.hide()"/>
</p:column>
</p:dataTable>
My bean:
private String mail1;
private String mail2;
private String mail3;
public List<Building> getBuildingList2() {
buildingList2 = getBldRepo().findByLocationId(lid);
return buildingList2;
}
Can anyone know how to fix it? I wont to send e-mail on choosen address not just on first on my list. Thanks
You seem to expect that only the current row is submitted when you press the command button in the row. This is untrue. The command button submits the entire form. In your particular case, the form is wrapping the whole table and thus the dropdown in every single row is submitted.
However, the value attribute of all those dropdowns are bound to one and same bean property instead of to the currently iterated row.
The consequence is, for every single row, the currently selected value is set on the bean property, hereby everytime overriding the value set by the previous row until you end up with the selected value of the last row.
You've here basically a design mistake and a fundamental misunderstanding of how basic HTML forms work. You basically need to move the form to inside the table cell in order to submit only the data contained in the same cell to the server.
<p:dataTable ...>
<p:column>
<h:form>
...
</h:form>
</p:column>
</p:dataTable>
If that is design technically not an option (for example, because you've inputs in another cells of the same row, or outside the table which also need to be sent), then you'd need to bind the value attribute to the currently iterated row instead and pass exactly that row to the command button's action method:
<p:dataTable value="#{additionalOrdersBean.additionalOrdersList}" var="additionalOrders" ...>
<p:column>
<p:selectOneMenu value="#{additionalOrders.mailTo}" ...>
...
</p:selectOneMenu>
...
<h:commandButton value="#{loc['send']}" action="#{additionalOrdersBean.sendProtocol(additionalOrders)}" ... />
</p:column>
</p:dataTable>
It's by the way not self-documenting and quite confusing to have a plural in the var name. Wouldn't you rather call it additionalOrder? Or is the javabean/entity class representing a single additional order really named AdditionalOrders?
Unrelated to the concrete problem: doing business logic in getter methods is killing your application. Just don't do that. See also Why JSF calls getters multiple times.

JSF PrimeFaces inputText inside dataTable

JSF-2.0, Mojarra 2.1.19, PrimeFaces 3.4.1
Summary of the problem: Have a p:inputText inside p:dataTable and inputText action fired by p:remoteCommand which passes the dataTable row index as a parameter with f:setPropertyActionListener. But it always passes the last row of the dataTable, not the index of the row which includes currently clicked p:inputText.
As it can be seen from my previous questions, I am trying to use p:inputText as a comment taker for a status like in Facebook or etc. Implementation includes a p:dataTable. It's rows represents each status. Seems like:
<p:dataTable id="dataTable" value="#{statusBean.statusList}" var="status"
rowIndexVar="indexStatusList">
<p:column>
<p:panel id="statusRepeatPanel">
<p:remoteCommand name="test" action="#{statusBean.insertComment}"
update="statusRepeatPanel">
<f:setPropertyActionListener
target="#{statusBean.indexStatusList}"
value="#{indexStatusList}">
</f:setPropertyActionListener>
</p:remoteCommand>
<p:inputText id="commentInput" value="#{statusBean.newComment}"
onkeypress="if (event.keyCode == 13) { test(); return false; }">
</p:inputText>
</p:panel>
</p:column>
</p:dataTable>
Upper code says when the press enter key, fire p:remoteCommand which calls the insert method of the managed bean.
#ManagedBean
#ViewScoped
public class StatusBean {
List<Status> statusList = new ArrayList<Status>();
public int indexStatusList;
public String newComment
//getters and setters
public void insertComment() {
long statusID = findStatusID(statusList.get(indexStatusList));
statusDao.insert(this.newComment,statusID)
}
Let's debug together; assuming there are three statuses shown in the p:dataTable, click in the p:inputText which in the second status(index of 1), type "relax" and press the enter key.
In the debug console, it correctly shows "relax", but it finds the wrong status because indexStatusList has the value of 2 which belongs the last status in the p:statusList. It must be 1 which is the index of p:inputText that clicked on the dataTable row.
I think problem is about p:remoteCommand which takes the last index on the screen.
How it works?
Let's imagine there is a p:commandLink instead of p:remoteCommand and p:inputText:
<p:commandLink action=#{statusBean.insertComment>
<f:setPropertyActionListener target="#{statusBean.indexStatusList}"
value="#{indexStatusList}"></f:setPropertyActionListener>
This component successfully passes the indexStatusList as currently clicked one.
Conceptual problem in this solution lies in way how p:remoteCommand works. It creates JavaScript function whose name is defined in name attribute of p:remoteCommand. As you putted this in dataTable it will iterate and create JavaScript function called test as many times as there is rows in this table, and at the end last one will be only one. So, solution can be in appending index at the name of the remoteCommand but that is bad, because you will have many unnecessary JavaScript functions. Better approach would be to create one function an pass argument to it. So define remoteCommand outside of datatable:
<p:remoteCommand name="test" action="#{statusBean.insertComment}" update="statusRepeatPanel">
and call test function like this in your onkeypress event:
test([{ name: 'rowNumber', value: #{indexStatusList} }])
This will pass rowNumber parameter in your AJAX request. In backing bean's insertComment() method you can read this parameter and do with it anything you want:
FacesContext context = FacesContext.getCurrentInstance();
Map map = context.getExternalContext().getRequestParameterMap();
Integer rowNumber = Integer.parseInt(map.get("rowNumber").toString());
NOTE: as you are updating panel in each row, maybe you can change update attribute of remoteCommand to #parent so this will work for all rows.
EDIT: You can update the specific panel in specific row with following code in Java method:
RequestContext.getCurrentinstance().update("form:dataTable:" + rowNumber + ":statusRepeatPanel")

How to get different component ids for each dropdown in a row using jsf

I have data table like below:
<p:dataTable id="transactionTableID" binding="#{transactionReportBean.dataTable}"
value="#{transactionReportBean.summarizedDateWiseTransactionList}"
var="transacVAR" rowKey="#{transacVAR.OID}" style="float:center;">
<p:column headerText="#{build.reportSelection}">
<p:selectOneMenu id="" value="#{transactionReportBean.summaryTxnReportSelected}" >
<f:selectItem itemLabel="-Select One-" itemValue="-Select One-"/>
<f:selectItem itemLabel="#{build.matchedreport}" itemValue="#{build.matchedreport}"/>
<f:selectItem itemLabel="#{build.carryforwardreport}" itemValue="#{build.carryforwardreport}"/>
<f:selectItem itemLabel="#{build.exceptionreport}" itemValue="#{build.exceptionreport}"/>
</p:selectOneMenu>
<p:commandButton update="#form" value="Generate" ajax="false"
actionListener="#{transactionReportBean.getReportSelected}" />
</p:column>
</p:dataTable>
And an action listener method like below:
public void getReportSelected(){
if(this.SummaryTxnReportSelected.equalsIgnoreCase("-Select One-")||this.SummaryTxnReportSelected.equalsIgnoreCase(null)){
this.message = AlgoMessageHandler.getMessage(AlgoMessageHandler.USER_MSG, "ERR0048");
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,
this.getMessage(), AFTSConstants.BLANK_STRING));
} else {
this.selectedDtTxn= (TransactionsSummaryReportVO) dataTable.getRowData();
System.out.println("listener called "+this.getSummaryTxnReportSelected()+" Selected transaction ID "+selectedDtTxn.getExecutionID());
String reportname = generateJasperReport(this.getSummaryTxnReportSelected(),AFTSConstants.SUMMARY_TXN_REPORT,this.selectedDtTxn);
System.out.println("Report Name"+reportname);
this.summaryReportStored= AFTSConstants.SUMMARY_REPORT_STORED_PATH+reportname+".pdf";
System.out.println(this.summaryReportStored);
this.setRenderGenerateButton(false);
}
}
That method is about to generate report based on the dropdown item we are selecting. In my table I have 10 rows, each row contains one dropdown having 3 items. There is a "generate" button. After selection of dropdown items and clicking the "generate" button, for first 9 rows it doesn't give component IDs and for 10th row it's working.
Here the problem is not about generating report, the problem is JSF doesn't take different component IDs for each dropdown in each row. I tried id="reportID", but no success. I tried to give row key value of table rowKey="#{transacVAR.OID}" as id="#{transacVAR.OID}", but it throws an exception like "empty component id".
How am I supposed to solve this problem?
Your problem has nothing to do with component IDs. Your problem is caused because you're binding submitted values of all rows to one and same bean property. JSF processes the submitted values based on the order the input elements appear in the tree. So, JSF will call the very same setter method with the submitted value for every single row until the last row is reached. You end up with the value of the last row. If you have placed a breakpoint on the setter method, you'd have noticed that it's subsequently been called with different values from every individual row.
You need to bind the value to the currently iterated row object instead.
<p:selectOneMenu value="#{transacVAR.summaryTxnReportSelected}">

JSF Rich scroallable Datatable Issue

In a scrollable datatable, which displays a collection of objects , I have to add one manual row as first row. That row contains inputText, through which user can able to enter some values and while hitting enter, those values will save and displays in bottom rows. I couldnt able to add this manual row as first row. Below is the current code.
<rich:scrollableDataTable value="{resultList}" var="result">
<rich:column>
<f:facet name="header">Name</f:facet>
<h:outputText value="#{result.name}" />
</rich:column>
<rich:column>
<f:facet name="header">Category</f:facet>
<h:outputText value="#{result.category}" />
</rich:column>
</rich:scrollableDataTable>
The above code is for just displaying the values from the backend.
1) For that you have to use bind rich:scrollableDataTable with the instance of HtmlScrollableDataTable in the backing bean.
In backing bean, create instance of it with accessor methods & then you can initialize it accordingly by adding the inputText components. Add actionListeners to these input components & then in listeners, you can add these inputText values as outputText to the table again as rows accordingly.
2) Else you can use inputText rather then outputText & disabling the subsequent rows other then 1st, so only data may get displayed - preventing input.
<rich:scrollableDataTable value="{resultList}" var="result">
<rich:column>
<f:facet name="header">Name</f:facet>
<h:inputText value="#{result.name}" disabled ="#{!result.isFirstRow}"/>
</rich:column>
<rich:column>
<f:facet name="header">Category</f:facet>
<h:inputText value="#{result.category}" disabled ="#{!result.isFirstRow}"/>
</rich:column>
</rich:scrollableDataTable>
Backing Bean :
//---
public void initialize(){
resultList.add(new Result("", "", true)); // Setting 1st input row enabled
}
public void inputListener(ActionEvent event){
// appending object based on input to the resultList
resultList.add(new Result(inputName, inputValue, false));
// added a boolean field to identify rows added later & to make them enable/disable accordingly
}
//---
I am not familiar with Richfaces, but tried to achieve it, as I was doing it with IceFaces.

Resources