JSF 2.2 dynamic h:selectOneMenu in p:dataTable submit old value - java

I'm stuck on this current Problem, maybe somebody can help me out on this:
I try to build a dynamic Ruleseditor based on JSF 2.2 and the latest primefaces version (5.0).
Therefor I construct a p:dataTable which display rule values in seperate columns. To edit the values the Editor generates a pickable Valuelist from the used database table.
Everything works fine but if I try to select another value in the pickable value list and click the save Button the old Value is submitted to the bean and the h:selectOneMenu display the last used value.
I tried many solutions but this is the current on JSF Side:
<h:form id="ruleTableForm">
<p:dataTable value="#{table.rules}" var="rule">
<p:columns var="column" value="#{table.columns}" headerText="#{column.header}" width="250">
<h:selectOneMenu id="columnSelectMenu" value="#{rule.ruleColumnToValueMap[column].value}">
<f:selectItems value="#{column.pickList.entrySet()}"
var="pickItem" itemLabel="#{pickItem.value}"
itemValue="#{pickItem.key}" />
</h:selectOneMenu>
</p:columns>
</p:dataTable>
<div class="button_group">
<h:commandLink styleClass="add_button" action="#{regelEditorViewController.saveRuleset()}">Save</h:commandLink>
</div>
</h:form>
The parts used in the xhtml-File from my ViewController look like this:
#ManagedBean
#ViewScoped
public class RegelEditorViewController implements Serializable {
private RegelEditorViewData viewData;
#Produces
public RegelEditorViewData getViewData() {
if (this.viewData == null) {
this.viewData = this.viewDataService.produceViewData(0L,20L);
}
return this.viewData;
}
public void saveRuleset() {
RulesetViewData rulesetViewData = this.viewData.getCurrentRuleset();
this.viewDataService.saveRuleset(rulesetViewData);
this.refreshViewData();
}
}
The ruleColumnToValueMap is a Map:
private Map<RuleTableColumnViewData, Value> ruleColumnToValueMap = new LinkedHashMap<RuleTableColumnViewData, Value>();
...and the "pickList" in the "ruleTableColumnViewData"-object is also a Map with Strings:
private Map<String, String> pickList = new LinkedHashMap<String, String>();
The solutions I tried and but do not worked:
work with an ajax setter, which select the value (but the value is null in the setter method)
<f:ajax listener="#{regelEditorViewController.selectedRuleColumnValueChanged(table, rule, column, rule.ruleColumnToValueMap[column].value)}" render="#form" />
work with an immediate ajax setter, which sends always the last setted value to the bean:
<f:ajax listener="#{regelEditorViewController.selectedRuleColumnValueChanged(table, rule, column, rule.ruleColumnToValueMap[column].value)}" immediate="true" render="#form" />
tried to work with p:selectOneMenu but it seems to be very buggy: it is not possible to change the selected item, the component do not show a menu
tried to work with "SelectItem" in a "pickList" List and a "SelectItem" for the column in the "ruleColumnToValueMap" Map
Maybe someone have a possible solution that can help me?

The example above this answer is working correctly, the failure was that the controller do not save the data to the database!

Related

Data is not provided correctly after doing a request in JSF

In the view main_app.xhtml there are 5 different fields, each of them contains a button, when clicking in this button it has to open the view chart_xxx.xhtml, and it will show a chart with the different data related to the field
i.e.:
General Rating will show some data in the Chart
Clean Rating will show some different data in the Chart
etc
When I click the button the buildGraph() method is called several times, usually 2 or 3, and it some of those requests the parameter sent in the request is shown and in other is not, I don't know why this is happening, therefore
I decided to store that parameter sent by the request in the session but... I don't know why, if I go back to main_app.xhtml, click in a different button, the view chart_xxx.xhtml will show the data from the previous request sent,
but if I go back again to main_app.xhtml and click again in the same button, the correct data is shown.
For example:
I click on General Rating button, its data is shown on the chart.
Go back to main_app.xhtml
Click on Clean rating
Data from General Rating is shown.
Go back to main_app.xhtml
Click on Clean rating again
Data from Clean rating is shown correctly
I have read this topic Why JSF calls getters multiple times several times but I couldn't find a way to adapt it to my feature.
Furthermore, if I use #RequestScoped, the chart is shown empty the second time I click on a button to show it.
Please, omit syntactic issues, I had to change some names due to my company policies so it cannot be identified and also translate from my language so there might be some syntactic issue but only from the translation.
I also omitted Service and DAO classes because they are working fine, they return what I expect from them.
I work with Java 1.6, JSF 2.1 and Primefaces 6.0
So, I need help solving this issue or if someone knows a better approach, I am all eyes but in this case, please, try to make an extended answer because I really cannot think about any other way.
EDIT 1
I've removed Setter and Getter in order to make my code more clear for you, also removed some code assigning attributes to the Chart, but all these things are in my original code and working fine.
PollGraphicBean.java
#ManagedBean(name="pollGraphic")
#SessionScoped
public class GraficosEncuestaBean {
private LineChartModel graphicLine;
public GraficosEncuestaBean(){
if(graphicLine == null){
this.buildGraph();
}
}
public void buildGraph(){
HttpServletRequest parameterMap = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true);
String general = "";
String clean = "";
String date1 = "01-03-2017 00:00";
String date2 = "30-04-2017 00:00";
if(parameterMap != null){
if(general != null){
general = parameterMap.getParameter("general");
session.setAttribute("hidden", general);
}else if(clean != null){
clean = parameterMap.getParameter("clean");
session.setAttribute("hidden", clean);
}
this.buildGraphicFinal(date1,date2, (String) session.getAttribute("hidden"));
}
}
private void buildGraphicFinal(String date1, String date2, String value){
this.graphicLine = new PollGraphicService().getGraphicData(date1, date2, value);
}
}
chart_xxx.xhtml
<ui:define>
<p:panel>
<h:form>
<p:chart type="line" model="#{pollGraphic.graphicLine}" />
</h:form>
</p:panel>
</ui:define>
main_app.xhtml
<ui:define>
<p:panelGrid>
<p:column>
<p:panel>
<div>
<h:form prependId="false">
<input type="hidden" value="General Rating"
name="general" id="general"/>
<p:commandButton value="Get results"
actionListener="#{pollGraphic.buildGraph}" onstart="location.href =
'/XXX/chart/chart_xxx.xhtml'"/>
</h:form>
</div>
</p:panel>
</p:column>
<p:column>
<h:form prependId="false">
<input type="hidden" value="Clean Rating"
name="clean"/>
<p:commandButton value="Get results"
actionListener="#{pollGraphic.buildGraph}" onstart="location.href =
'/XXX/chart/chart_xxx.xhtml'"/>
</h:form>
</div>
</p:panel>
</p:column>
</p:panelGrid>
</ui:define>
Try changing #SessionScoped to #ViewScoped or even #RequestScoped

Primefaces 4.0 - Paginated Data Table Filtering issues

I built a Relatively simple DataTable and tried to use the filter feature with also using the pagination feature.
in reference to the primefaces showcase i created a column for each field in my Customer class.
this is my "Controller" Bean:
#SessionScoped
#Named
public class CustomerListController implements Serializable{
public static final long serialVersionUID = //UID;
private List<Customer> filteredCustomers;
private List<Customer> allCustomers;
public CustomerListController(){
//some Class that generates a list of sufficiently many
//dummy customers on instantiation
this.allCustomers = new CustomerListProducer().getCustomers();
}
public List<Customer> getFilteredCustomers{
return this.filteredCustomers;
}
public void setFilteredCustomers(List<Customers> list){
this.filteredCustomers = list;
}
public List<Customer> getAllCustomers(){
return this.allCustomers;
}
}
i use following dataTable to render this:
<p:dataTable paginator="true" rows="18" scrollRows="15" scrollable="true"
scrollHeight="500" var="customer" value="#{customerListController.allCustomers}"
scrollable="true" id="customerTable"
filteredValue="#{customerListController.filteredCustomers}" widgetVar="table">
<f:facet name="header">
<p:outputPanel>
<h:outputText value="Search all fields:" />
<h:inputText id="globalFilter" onkeyup="table.filter()" />
</p:outputPanel>
</f:facet>
<p:Column id="nameColumn" filterBy="name" sortBy="name"
headerText="Customer" filterMatchMode="contains">
<h:outputText value="#{customer.name}" />
</p:Column>
<!-- Some more columns in the exactly same
manner as this changes only in Customer attribute-->
</p:dataTable>
When i press any Key in any given filter field the Table loses all rows and even upon clearing the fields does not display any.
When refreshing the page i get the expected amount of rows & pages.
I will try to provide amendments as requested.
EDIT:
I am using Primefaces version 4.0.0 as installed with maven.
I have been digging into console under FF and found the following:
The response XML is empty save the node Entry for updated table. There are no JavaScript errors thrown and the viewstate id sent with the "table data" changes with every keystroke.
Your filterBy and sortBy must contain a deferred EL expression.
<p:Column id="nameColumn" filterBy="#{customer.name}" sortBy="#{customer.name}"
headerText="Customer" filterMatchMode="contains">
<h:outputText value="#{customer.name}" />
</p:Column>
UPDATE:
Since I could confirm that both EL and non EL approach works in PF V4.0, here is another possible answer to your problem:
Please check your SessionScoped import.
I could not make it work using the javax.faces.bean.SessionScoped.
Using javax.enterprise.context.SessionScoped it worked.
Personally I avoid as much as possible the usage of SessionScoped scope, try to use the ConversationScoped instead. It will make a better usage of your server resources.
Replace your code with
public CustomerListController(){
//some Class that generates a list of sufficiently many
//dummy customers on instantiation
this.allCustomers = new CustomerListProducer().getCustomers();
this.setFilteredCustomers(this.getAllCustomers());
}
No value is provide to the filterAttribute therefore you see a blank dataTable when you do filter.
Hope this help's

JSF Pass OutputText and InputText value to a property

This is my second day working with JSF. Have no previous background in Java , Have Been working with Flex and C++ for quite some time. Some history so that everybody knows where im coming from . For a "rush" project i am running into an issue
<h:panelGroup id="txeTab" layout="block" class="txeTab">
<h1>TXE</h1>
<h:form id="txeForm">
<h:panelGrid columns="3">
<c:forEach items="${txeConfBean.getListTable()}" var="property">
<h:outputLabel id="key" value="${property.key}"/>
<h:inputText id="value" value="${property.value}" />
<h:commandButton value="Change" action='${txeConfBean.setProperty('key','value')}'/>
</c:forEach>
</h:panelGrid>
</h:form>
</h:panelGroup>
and The Bean is as follows
public HashMap <String,String> getListTable ()
{
String[] keys = new String[super.keyData.size()];
HashMap <String,String> retKeys = new HashMap <String, String>();
super.keyData.toArray(keys);
for (int i=0;i<keys.length;i++)
{
if(!keys[i].isEmpty())
{
retKeys.put(keys[i],getProperty(keys[i]));
}
}
return retKeys;
}
im able to display the Key,value pairs recursively. But i want to update a specific key with new value once someone updated the h:inputText id="value" value="${property.value}" /> and press the command button the new value is written to. Need help in this regard . Googling it make me feel there are too many ways to do it. Need help. I am just unable to figure out what to pass to ${txeConfBean.setProperty('key','value')} How can i pass the value of both InputText and OutPutText to setProperty ?
The ${}, which is inherited from legacy JSP, can't do a "set" operation on a property. It can only do a "get" operation on the property.
If you want to support both "get" and "set" on a property, you need the #{}. Even more, in general in JSF, you should not use ${} anymore. Further, in order to get/set a map value by an input component, you have to reference the map value by its key using the brace notation as #{bean.map[key]}.
<h:form id="txeForm">
<h:panelGrid columns="3">
<c:forEach items="#{txeConfBean.listTable}" var="property">
<h:outputLabel id="key" value="#{property.key}"/>
<h:inputText id="value" value="#{txeConfBean.listTable[property.key]}" />
<h:commandButton value="Change" />
</c:forEach>
</h:panelGrid>
</h:form>
Note that the command button action is omitted. JSF/EL will "automagically" already call the map's put() method when the model value is about to be updated. It's unnecessary in this particular construct. Also note that in this construct, the entire form is submitted. You might want either to put the command button outside the table, or to use <f:ajax> to submit the current "row" only.
See also:
Difference between JSP EL, JSF EL and Unified EL
Unrelated to the concrete problem: you're doing the business job in a getter method. This is extremely inefficient in case the property is referenced in iterating components. Do the business job in (post)constructor instead. It'll be invoked only once. This way you can make the getter really a fullworthy getter.
public HashMap<String,String> getListTable() {
return listTable;
}
See also:
JSF calling setter & getter multiple times.

JSF GAE: Value Update Problem in managed bean method

I have the following piece of code with a simple h:outputText pointing to a int and a p:commandLink to set a value:
<h:form id="f1">
<h:outputText id="text" value="#{testBean.index}"/>
<p:commandLink actionListener="#{testBean.test}" update="text">
<f:setPropertyActionListener target="#{testBean.index}" value="5" />
<h:graphicImage url="/images.png"/>
</p:commandLink>
</h:form>
The managed bean looks like this:
#javax.faces.bean.ManagedBean #ViewScoped
public class TestBean implements Serializable{
private int index; // getter/setter
#PostConstruct public void init() {
index = 0;log.log(Level.WARNING, "#PostConstruct");}
public void test(ActionEvent ae){
log.log(Level.WARNING, "Index: "+index);}
}
The bean is constructed correctly, and after the first click on the image the h:ouputText is updated to 5. But in my log message I only see Index: 0 during the first click on the image.
It's something similar like Jsf updates model value with old value, but I have the JSF #ManagedBean annotation.
Action listeners are invoked in the order they're definied in the view. You want to use action instead of actionListener. Even more, the action should in first place have been used to invoke a business action.
<p:commandLink action="#{testBean.test}" update="text">
<f:setPropertyActionListener target="#{testBean.index}" value="5" />
<h:graphicImage url="/images.png"/>
</p:commandLink>
See also:
Differences between action and actionListener
What is happening is that the test ActionEvent is getting fired before the request values have been applied.
To get a better understanding of the JSF phase lifecycle and when lifecycle events and ActionEvents fire, implement the Debug PhaseListener as specified in the following blog article.
http://balusc.blogspot.com/2006/09/debug-jsf-lifecycle.html
This should help you understand when request values are being applied, and when events are being fired.

Adding a Dropdown listbox - Issue beginner

I have asked this question several times in this and various other forums, but still unable to implement it in my code.
I am doing this example , and i need to add a listbox (like in the column MANUFACTURE).
i am unable to display the Listbox or populate it with values from my Java class.
My java code looks like this;
private List<Hotel> listHotel;
public List<Hotel> ListAllHotels() {
return dml.displayAllHotels(); //dml.displayAllHotels() returns a List<Hotel>
}
Normally i create a listbox and populate it with values using the following JFS code;
<h:selectOneMenu value="#{HotelDataForm.stationedHotel}" id="globalFilter" onchange="carsTable.filter()" >
<f:selectItems value="#{HotelDataForm.ListAllHotels}" var="user" itemValue="#{user[1]}" itemDisabled="false" itemLabel="#{user[1]}" />
<h:outputText value="#{carsTable[1]}" />
</h:selectOneMenu>
And this works, but i am unable to add this code to the Manufacturer column in the link i posted. In the example they make use of SelectItem[] object to populate the listbox. I am clueless as in how to add and populate values to the manufacturer column in my program.
This is from the example on Page 131 of the PrimeFaces 2.2 Guide
If you’d like to use a
dropdown instead of an inputtext to only allow predefined filter values use filterOptions attribute
and a collection/array of selectitems as value. In addition, filterMatchMode defines the built-in
matcher which is startsWith by default. Following is an advanced filtering datatable with these
options demonstrated.
<p:column
filterBy="#{car.manufacturer}"
headerText="Manufacturer"
filterOptions="#{carBean.manufacturerOptions}"
filterMatchMode="exact">
<h:outputText value="#{car.manufacturer}" />
</p:column>
So in this example, the carBean should have a method getManufacturerOptions() that returns either SelectItem[] or List<SelectItem> containing all the values that should be in the filter dropdown list.
REFERENCE: Javadoc for SelectItem

Categories

Resources