JSF h link takes last item in list instead of current - jsf

I got a list of objects displayed in a page.
This is the view:
<h:form>
<h:dataTable var="o" value="#{opleidingController.opleidingen}" border="0" >
<h:column>
<f:facet name="header">Code</f:facet>
<h:link value="#{o.opl_code}" outcome="#{opleidingController.getOpleidingByCode(o.opl_code)}" />
</h:column>
<h:column>
<f:facet name="header">Titel</f:facet>
<h:outputText value="#{o.opl_titel} "></h:outputText>
</h:column>
<h:column>
<f:facet name="header">Thema</f:facet>
<h:outputText value="#{o.opl_thema} "></h:outputText>
</h:column>
</h:dataTable>
</h:form>
This displays everything very well.
Like you can see I got the code part of the object set as a link.
With this code I get an object out of the database, from which I display the details.
The problem here is that every link displays the details of the last object of the list instead of the current object where I click the link.
This is the function I call (which works):
public String getOpleidingByCode(String code) {
this.opleiding = this.opleidingFacade.getOpleidingByCode(code);
return "details";
}
I hope any of this makes any sense?
Sorry if the solution is super clear but I am new to all of this.
Thanks in advance!
Kind regards
Deflandrex
EDIT:
This is the function that I use to call the first view (from which I provided the code)
public String getOpleidingenOpThema() {
this.opleidingen = this.opleidingFacade.getOpleidingenOpThema();
return "resultaat";
}
Both the functions are located in 1 controller.

What happens with the current code :
The outcome value is processed for each link element in the datatable sequentially by calling the opleidingController. getOpleidingByCode(o.opl_code). The backing bean method sets the this.opleiding value corresponding to the o.opl_code. Once all the links's outcome values are processed this.opleiding holds the value of the last o.opl_code. Hence, you are seeing last value from the list in the details page.
One Proposed solution:
The this.opleiding value should be set in the controller based on the o.opl_code when you process the details after clicking the link and not when the link is displayed and the outcome is set. You need to
Set outcome="details" in the h:link tag as this value is static and does not change based on any logic in the backing bean and remove method binding.
Pass the value of o.opl_code as a request parameter to the link as given below within the h:link tag.
<f:param name="opl_code" value="#{o.opl_code}"/>
Have the detail backing bean load the this.opleiding value based on the request parameter opl_code. This way you will have details for specific opl_code based on what link you click.

Related

Evaluation with <c:when> in data table doesn't work

I have searched and tried but this time I'm really stuck.
I am trying to build a simple datatable (primefaces) where the cell content can be of different types. There is a holder class for each cell content that can hold different entities and I want to use < c:choose> to check and display the right entity in the entity specific way.
The code looks like:
<p:dataTable emptyMessage="" value="#{date.getThreadContent(column.propertyID)}" var="content">
<p:column>
#{content.type}
<c:choose>
<c:when test="#{content.type == 'activity'}">
#{content.activity.name}
</c:when>
<c:when test="#{content.type == 'todo'}">
#{content.activity.name}
</c:when>
<c:otherwise>
Neither activity or todo
</c:otherwise>
</c:choose>
</p:column>
</p:dataTable>
The EL evaluation doesn't work regardless if I try a boolean or String values in the bean (I'm using String value in the code above).
So each time the content is not empty, the output first displays the correct content type from #{content.type} but then together with 'Neither activity or todo'.
I can also say that this data table itself represents a cell content in a parent data table (list of entities per date) that is not visible in this code as I try to isolate the problem.
Could it be that this type of expression cannot be done in a ?
Any help is appreciated.
Use JSF rendered tags to do your conditional display logic, it removes it from the DOM completely and makes validations easier.
Refer: http://docs.oracle.com/javaee/6/tutorial/doc/bnaik.html for EL expressions
Also a good explanation is provided by BalusC here: JSTL c:if doesn't work inside a JSF h:dataTable
<p:dataTable emptyMessage="" value="#{date.getThreadContent(column.propertyID)}" var="content">
<p:column>
#{content.type}
<h:outputText value="#{content.activity.name}" rendered="#{content.type == 'activity' or content.type == 'todo'}" />
<h:outputText value="Neither activity or todo" rendered="#{content.type != 'activity' and content.type != 'todo'}" />
</p:column>
</p:dataTable>

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.

Two buttons ( that generate inputtexts when clicked ) don't work on the same Facelet

I have two buttons that generate inputtexts when clicked. The problem is, when these two buttons are on the same xhtml, wichever is the first one, it is the only one that works.
If you need, here is an explanation on the Code 1 below:
It represents two inputtexts to get the name
and email of the author of a Document. moreAuthors represents
the method getMoreAuthors() that belongs to the Managed Bean inserirBean.
getMoreAuthors() returns a List<AuthorBean>.
The List<AuthorBean> is a property from the Managed Bean inserirBean.
The class AuthorBean creates the instances of Author, it has a property called
Author author.
AddAuthor() adds one more AuthorBean to the List<AuthorBean> in
Managed Bean inserirBean.
If you need, here is an explanation on the Code 2 below:
It follows the same structure from Code 1. Here, there's only one
inputtext that is used to get a subject of a Document.
moreSubjects returns a List<SubjectBean>. The class SubjectBean
creates the instances of the class Subjects.
SubjectBean has a property Subject sub,
and Subject has a property called String subject.
AddSubject() adds one more SubjectBean to the List<SubjectBean> in
Managed Bean inserirBean.
Code 1- If this is the only one on the xhtml, it works:
<h:form >
<h:dataTable value="#{inserirBean.moreAuthors}" var="authorBean">
<h:column>
Nome do Autor:
<h:inputText value="#{authorBean.author.name}" />
</h:column>
<h:column>
Email do Autor:
<h:inputText value="#{authorBean.author.email}" />
</h:column>
<f:facet name="footer">
<h:panelGroup>
<h:commandButton value="+" action="#{inserirBean.addAuthor()}" />
</h:panelGroup>
</f:facet>
</h:dataTable>
</h:form>
2- If this is the only one on the xhtml, it works too:
<h:form >
<h:dataTable value="#{inserirBean.moreSubjects}" var="subjectBean">
<h:column>
Palavras-chave:
<h:inputText value="#{subjectBean.sub.subject}" />
</h:column>
<f:facet name="footer">
<h:panelGroup>
<h:commandButton value="+" action="#{inserirBean.addSubject}" />
</h:panelGroup>
</f:facet>
</h:dataTable>
</h:form>
BUT, if I put these two blocks on the same xhtml, the one that appears first on the browser, always works, and the second, never - and I've already tried to change their position, but whichever I put first place on screen is the one that works.
What's causing this problem?
Thank you!
I just put the two datatables in code 1 and 2 inside the same <form> block and both worked.

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 save h:inputText values of a h:dataTable? My attempt only saves the value of last row

I'm having trouble making a dataTable where each row has a inputText and a commandLink. When the link is clicked, only it's row's inputText's data is submitted.
Something like this?
<h:dataTable value="#{bean.items}" var="item">
<h:column>
<h:inputText value="#{bean.value}"/>
</h:column>
<h:column>
<h:commandLink action="#{bean.save}" value="save">
<f:setPropertyActionListener target="#{bean.item}" value="#{item}" />
</h:commandLink>
</h:column>
</h:dataTable>
Bean:
#RequestScoped
public class Bean {
private Item item;
private String value;
Right now, as it is, it's using the last row's inputText to fill the value. I wrapped another h:form, but it broke other things and I've learned that nested h:form is not the right way to do it hehe
What's the correct way to do this?
Thanks.
You're binding the value of all HTML input elements to one and same bean property. This is of course not going to work if all those HTML input elements are inside the same form. All values are subsequently set on the very same property in the order as the inputs appeared in the form. That's why you end up with the last value. You'd like to move that form to inside the <h:column> (move; thus don't add/nest another one).
The usual approach, however, would be to just bind the input field to the iterated object.
<h:inputText value="#{item.value}"/>
An alternative, if you really need to have your form around the table, is to have a Map<K, V> as bean property where K represents the type of the unique identifier of the object behind #{item} and V represents the type of value. Let's assume that it's Long and String:
private Map<Long, String> transferredValues = new HashMap<Long, String>();
// +getter (no setter necessary)
with
<h:inputText ... value="#{bean.values[item.id]}" />
This way you can get it in the action method as follows:
String value = values.get(item.getId());
By the way, if you happen to target Servlet 3.0 containers which supports EL 2.2 (Tomcat 7, Glassfish 3, etc), then you can also just pass the #{req} as a method argument without the need for a <f:setPropertyActionListener>.
<h:commandLink ... action="#{bean.save(item)}" />
See also:
How and when should I load the model from database for h:dataTable
How can I pass selected row to commandLink inside dataTable?
How to dynamically add JSF components

Resources