Why JSF checkBox inputs new value to map - jsf

I have a checkBox inside dataTable in my JSF page with value, which it gets from map which is in bean.
<h:selectBooleanCheckbox id="rowChecked"
value="#{messagesListBean.selectedRowIndexesMap[rowIndex]}">
<a4j:ajax event="click" execute="#this" render="#none"
limitRender="true"
listener="#{messagesListBean.rowChecked(rowIndex, rec)}">
</a4j:ajax>
</h:selectBooleanCheckbox>
<h:outputLabel for="rowChecked" class="checkbox-style" />
Map is initialized when page loads and it initializes just fine - has 4 values, all of them are false. Map itself looks like this:
public Map<Integer, Boolean> selectedRowIndexesMap = new HashMap<Integer, Boolean>();
The problem is when I press one of the checkBoxes. It should edit value inside the map according to rowNumber. What it does is it add completely new element to the map, it does not edit it.
For example, if before clicking checkbox map looked like this: 0-false; 1-false; 2-false; 3-false;
After clicking the checkbox, it looks like this: 0-true; 0-false; 1-false; 2-false; 3-false;
Why is this happening?

Because numbers in EL are by default evaluated as Long. I.e. #{rowIndex} is actually Long.
A Long value of 0L does not equal an Integer value of 0. Evidence is below:
Long zeroL = 0L;
Integer zero = 0;
System.out.println(zeroL.equals(zero)); // false
So it simply inserts a new map entry with 0L as key instead of replacing the value associated with key of 0. Change it to a Map<Long, Boolean> and it'll work. This problem is unrelated to JSF checkbox.

Related

How to get value from Map<TestClass,Object> in item label

Below is the jsf code
<h:selectOneMenu>
<f:selectItems value="#{bean.mapObject}"var="entity"
itemValue="#{entity.key}" itemLabel="#{entity.value.code}"/>
</h:selectOneMenu>
Backing bean has map defined as
private Map<TestClass, Object> mapObject;
TestClass has two variables and getters/setters
String code;
String name;
Issue I am having is : On JSF, on Selectonemenu, for itemLabel i would like to display the TestClass String value i.e code should be displayed.
Presently on SelectOneMenu on UI, I am getting
TestClass[code=t,name=anu] on the drop down, instead i want code value i..e 't'
Please let me know how to acheive this.
The <f:selectItems var> should in case of maps specify the Map.Entry. The <f:selectItems value> should therefore specify Collection<Map.Entry>. This is not natively recognized yet when you just specify a Map (will come in JSF 2.3).
You basically need to explicitly set the value to Map#entrySet() as below in case you intend to use map key as item value and ma:
<h:selectOneMenu>
<f:selectItems value="#{bean.mapObject.entrySet()}"var="entry"
itemValue="#{...}" itemLabel="#{...}" />
</h:selectOneMenu>
The Map.Entry object has getKey() and getValue() methods which return respectively the map entry's key and map entry's value, which are in your case respectively TestClass and Object. You seem to want to display the code property of TestClass as item label. So, this should do:
<h:selectOneMenu>
<f:selectItems value="#{bean.mapObject.entrySet()}"var="entry"
itemValue="#{...}" itemLabel="#{entry.key.code}" />
</h:selectOneMenu>
You're not terribly clear on what exactly you'd like to use as item value, so I've left it open.
See also:
Our selectOneMenu wiki page
You need to change the itemlabel attribute, (with current value entity.value.code). It should be replaced by entity.key.code ?
Also the value needs to call the method .entrySet , because the map in itself is not a collection.
<h:selectOneMenu>
<f:selectItems value="#{bean.mapObject.entrySet()}" var="entry"
itemValue="#{entry.key}" itemLabel="#{entry.key.code}"/>
</h:selectOneMenu>
Alternatively, since it's the key you want, you could iterate the keys immediately.
<h:selectOneMenu>
<f:selectItems value="#{bean.mapObject.keySet()}" var="key"
itemValue="#{key}" itemLabel="#{key.code}"/>
</h:selectOneMenu>

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")

JSF / PrimeFaces selecting two rows from two p:dataTables arranged in a master-detail fashion fails with Request Scope

I stumbled upon a JSF / PrimeFaces problem and although I managed to get it working by changing the scope of the backing bean I would still like to understand why it failed in the first case. So, here's a narrowed-down example that reproduces the behavior:
We have a dead simple xhtml page that displays two p:dataTables in a form, one below the other. The top p:dataTable displays numbers and the second their divisors. So we have a classical master-detail view. A button allows us to update the page so when a new number is selected from the top table we can view its divisors on the bottom table:
<h:form id="NUMBERS-form">
<p:dataTable id="dt1" var="item" value="#{numbersController.divisorSets}"
rowKey="#{item}" rows="10" selection="#{numbersController.selectedDivisorSet}"
selectionMode="single">
<p:column>
#{item}
</p:column>
</p:dataTable>
<p:dataTable id="dt2" var="item" value="#{numbersController.divisors}"
rowKey="#{item}" rows="10" selection="#{numbersController.selectedDivisor}"
selectionMode="single">
<p:column id>
#{item}
</p:column>
</p:dataTable>
<p:commandButton id="Update" ajax="true" update=":NUMBERS-form"
action="#{numbersController.foo}" value="update"/>
</h:form>
The backing bean defines two read-only collections: one for the DivisorSets (i.e. the numbers whose divisors we want to find) and another one for the divisors of currently selected number. It also has two fields and property getters/setters for the currently selected number and the currently selected divisor of that number:
#ManagedBean
#ViewScoped // if this is toggled to #RequestScoped it stops working
public class NumbersController implements Serializable {
private static final Logger l = Logger.getLogger(NumbersController.class.getName());
public List<DivisorSet> getDivisorSets() {
List<DivisorSet> retValue = new ArrayList<DivisorSet>();
for (int i = 10 ; i < 20 ; i++)
retValue.add( new DivisorSet(i) );
return retValue;
}
public List<Integer> getDivisors() {
if (selectedDivisorSet != null)
return selectedDivisorSet.getDivisors();
else return null;
}
private DivisorSet selectedDivisorSet;
// getter and setter ...
private Integer selectedDivisor;
// getter and setter ...
public String foo() { return null; }
}
When the page first loads, only the top p:dataTable is populated. When a row of the top table is selected and the p:commandButton pressed, the divisors of that number are fetched on the bottom p:dataTable. So far so good. Here comes the problem: when a row is selected from the top table and a row also selected from the bottom table and the p:commandButton pressed, the logging messages I have in the setters reveal that:
when the scope of the backing bean is set to View both selected numbers are set correctly in the update model values phase
when the scope of the backing bean is set to Request only the selected number from the top table is set correctly, the setter for the selectedDivisor field (that is linked with the bottom p:dataTable) carries a value of 0 (or null in other examples I've tried with different classes used).
Note that there is no business logic in this trivial example to make it necessary to select a number from the bottom p:dataTable - this is just a narrowed-down version of the same problem I had in a real context. Can anybody explain the steps in the JSF lifecycle that result in the bottom table selected value not set properly when view is RequestScoped (as opposed to ViewScoped that succeeds)?

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

pagination in jsf

I would like your comments and suggestion on this. I am doing the pagination for a page in jsf. The datatable is bound to a Backing Bean property through the "binding" attribute. I have 2 boolean variables to determine whether to render 'Prev' and 'Next' Button - which is displayed below the datatable. When either the 'Prev' or 'Next' button is clicked, In the backing bean I get the bound dataTable property and through which i get the "first" and "rows" attribute of the datatable and change accordingly. I display 5 rows in the page. Please comment and suggest if there any better ways. btw, I am not interested in any JSF Component libraries but stick to only core html render kit.
public String goNext()
{
UIData htdbl = getBrowseResultsHTMLDataTable1();
setShowPrev(true);
//set Rows "0" or "5"
if(getDisplayResults().size() - (htdbl.getFirst() +5)>5 )
{
htdbl.setRows(5);//display 5 rows
}else if (getDisplayResults().size() - (htdbl.getFirst() +5)<=5) {
htdbl.setRows(0);//display all rows (which are less than 5)
setShowNext(false);
}
//set First
htdbl.setFirst(htdbl.getFirst()+5);
return "success";
}
public String goPrev()
{
setShowNext(true);
UIData htdbl = getBrowseResultsHTMLDataTable1();
//set First
htdbl.setFirst(htdbl.getFirst()-5);
if(htdbl.getFirst()==0)
{
setShowPrev(false);
}
//set Rows - always display 5
htdbl.setRows(5);//display 5 rows
return "success";
}
Please comment and suggest if there any better ways.
Well, that gives not much to answer on. It's at least not the way "I" would do, if you're asking for that. Long story short: Effective datatable paging and sorting. You only need Tomahawk (face it, it has its advantages). But if you're already on JSF2+Facelets instead of JSF1+JSP, then you can in fact also use ui:repeat and #ViewScoped instead of t:dataList and t:saveState.
We can use 'Repeat' component - this is similar to dataList or dataTable component in Primefaces
<p:repeat id="repeatComponent" var="education" value="#{backingBean.educationList}" emptyMessage="No records found">
<h:panelGroup>
<p:outputLabel for="center" value="Education Center:" />
<br />
<h:panelGroup>
<h:outputText id="center" value="#{education.centerName}">
</h:outputText>
</h:panelGroup>
</h:panelGroup>
</p:repeat>
This is similar to for loop in java
var - this act as loop iterator
value - takes list object
emptyMessage - takes String value, will get displayed when passed list object is empty

Resources