Primefaces: Editable object in accordionPanel with Dynamic = True - ajax

I am trying to achieve functionality where I am generating the tabs in according panel dynamically according to the list.
List is of say some object e.g Person
Now attribute corresponding to each person, I am showing in each tab in a form.
Values are loaded correctly but when I edit those value I am not getting updated values at the backing bean, I am getting only old values.
Can somebody please explain why ?
Here is the code.
Person Object
public class Person {
String name;
//getter setter
#Override
public String toString() {
return name;
}
}
Backing Bean
#ManagedBean(name = "editor")
public class EditorBean {
private List<Person> persons = new ArrayList<Person>();
#PostConstruct
void init() {
persons.add(new Person("dhruv"));
persons.add(new Person("tarun"));
}
public void testAction() {
System.out.println(persons);
}
// Getter Setter*****************//
public List<Person> getPersons() {
return persons;
}
public void setPersons(List<Person> persons) {
this.persons = persons;
}
}
XHTML Code:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui">
<h:head>
</h:head>
<h:body>
<h1>Hello World PrimeFaces</h1>
<h:form>
<p:accordionPanel value="#{editor.persons}" var="person" dynamic="true">
<p:tab title="#{person}">
<p:inputText value="#{person.name}"></p:inputText>
</p:tab>
</p:accordionPanel>
<p:commandButton action="#{editor.testAction()}" value="testAction"></p:commandButton>
</h:form>
</h:body>
</html>
When I run the same with with dynamic="false". This works fine. But in my real scenario I have lots of data which I cannot afford to paint in one go thats why I want to use dynamic = true.
Can somebody explain how to achieve this if not by dynamic = true ??

The reason you're having stale values in your accordion is that dynamic accordions default to cache="true". This attribute causes the accordion to not reload new/updated values. To cause an ajax refresh on tab open, set cache="false" on your accordion

Related

Variable in Bean is not holding its value through Button Clicks [duplicate]

This question already has an answer here:
#SessionScoped bean looses scope and gets recreated all the time, fields become null
(1 answer)
Closed 4 years ago.
This is my first question.
I am trying to increment a variable value in a textbox when a button is clicked using primefaces.
But through debug I found out that whenever I click the p:commandButton, EL bean function call, tries to increment the bean class variable "counter", whenever this happens the value of counter is always 0, so it gets incremented to 1 and that is shown in my webpage. It never reaches 2, 3, 4...
Below is the code:
xhtml:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head></h:head>
<h:body>
<h:form>
<h:panelGrid columns="2" cellpadding="5">
<h:outputLabel value="Counter:"/>
<h:outputText id="output" value="#{counterView.counter}" />
<p:commandButton value="Count" action="#{counterView.increment}" update="output" />
</h:panelGrid>
</h:form>
</h:body>
</html>
Java:
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.view.ViewScoped;
#ManagedBean(name="counterView")
#ViewScoped
public class counterView implements Serializable {
/**
*
*/
private static final long serialVersionUID = 2369382392318418242L;
private int counter = 0;
public int getCounter() {
return counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
public void increment() {
// Counter is 0 always the moment this function is entered...
this.counter = this.counter + 1;
}
}
I cant figure out where I am going wrong as its a classical example in primefaces showcase and I am following that...
Thanks in advance
You use the wrong ViewScoped annotation. You have to use the javax.faces.bean.* annotations in combination with #ManagedBean.
see: #SessionScoped bean looses scope and gets recreated all the time, fields become null

Why does altering my managed bean's definition result in the constructor not being called?

Please have a look at the following JSF page and at the definition of the managed bean class which it references.
When I run my app and load the page I get the results shown in the "Good Results" screen shot. Note that the page is displaying data and that the "Hello World" string printed by the bean's constructor appears in the console.
If I alter the bean's definition by commenting out the definition of the "junk" integer (near the top) then I get the results shown in the "Bad Results" screen shot. Now there is no data and, most tellingly, the "Hello World" string does not appear in the console. No errors are reported to the console. The page is being rendered but it appears as though the JSF engine has decided that it does not like the bean's definition and is therefore not going to use it (the constructor is not called).
I have tried mightily to produce a Minimal, Complete, and Verifiable example. I eliminated several forms from the JSF page (you can observe that much of the bean's code is no longer being referenced by the JSF page). I eliminated JPA from the picture by creating the DummyDataAccessService class. I tried to eliminate the use of one or more of my own custom classes (Order, Patient, Product, DataAccessService, and DummyDataAccessService) but I could not: almost any alteration of the bean's definition produces the same strange behavior that results from removing the definition of the "junk" member variable.
I created a custom logging.properties file which bumped the level to ALL. The logging produced by the Good vs. Bad cases were close to the same. The two "Logging Differences" screen shots below reveal the primary differences.
I do not know how to explore this further. I don't know enough to even conjecture what might be going on. Any clue or recommendation on a course of action will be greatly appreciated.
JSF Page
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<h:outputStylesheet library="default" name="css/style.css" />
<title>Managed Bean Problem</title>
</h:head>
<h:body>
<h3 class="title">Managed Bean Problem</h3>
<h:outputText value=" " />
<table align="center" width="600">
<tr><td><h:form>
<h:dataTable value="#{orderController.table}" var="order" styleClass="demo" columnClasses="columns, columns">
<h:column>
<f:facet name="header">
<h:column>
<h:outputText value="Patient"></h:outputText>
</h:column>
</f:facet>
<h:outputText value="#{order.patient.fullName}"></h:outputText>
</h:column>
<h:column>
<f:facet name="header">
<h:column>
<h:outputText value="Product"></h:outputText>
</h:column>
</f:facet>
<h:outputText value="#{order.product.name}"></h:outputText>
</h:column>
<h:column>
<f:facet name="header">
<h:column>
<h:outputText value="Actions"></h:outputText>
</h:column>
</f:facet>
<h:panelGrid columns="1">
<h:commandLink value="delete" action="#{orderController.delete}">
<f:setPropertyActionListener target="#{orderController.target}" value="#{order}" />
</h:commandLink>
</h:panelGrid>
</h:column>
</h:dataTable>
</h:form></td></tr>
</table>
</h:body>
Order Controller Managed Bean
package com.rvaessen.dmescripts.controllers;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.model.SelectItem;
import com.rvaessen.dmescripts.model.*;
#ManagedBean
public class OrderController {
private int junk; // Leave it in; Good. Comment it out; Bad.
private DataAccessService dataService = new DummyDataAccessService();
public OrderController() {
System.out.println("Hello, World");
}
#PostConstruct
public void init() {
initPatients();
initProducts();
initOrders();
}
// ********************************* The Orders Table ***************************************
private Order target;
private List<Order> table;
private void initOrders() { table = dataService.getOrders(); }
public List<Order> getTable() { return table; }
public void setTarget(Order order) { target = order; }
public String delete() {
dataService.removeOrder(target);
table.remove(target);
return null;
}
// ********************************* Add New Order ***************************************
// NOTE: The Add New Order methods are no longer referenced by the JSF page
private Order newOrder;
public String addNew() {
newOrder = new Order();
return null;
}
public String save() {
dataService.addOrder(newOrder, patient, product);
table.add(newOrder);
cancel();
return null;
}
public String cancel() {
newOrder = null;
return null;
}
public boolean getAddingNew() { return newOrder != null; }
/************************ The Patients Menu **********************************/
// NOTE: The Patients Menu methods are no longer referenced by the JSF page
private Patient patient;
private List<Patient> patients;
private void initPatients() {
patients = dataService.getPatients();
if (patients.size() > 0) patient = patients.get(0);
}
public List<SelectItem> getPatients() {
List<SelectItem> list = new ArrayList<SelectItem>();
patients.forEach(patient -> list.add(new SelectItem(patient.getId(), patient.getFullName())));
return list;
}
public Long getPatientId() {
return patient == null ? 0 : patient.getId();
}
public void setPatientId(Long id) {
patients.forEach(patient -> {
if (patient.getId() == id) {
this.patient = patient;
}
});
}
/************************ The Products Menu **********************************/
// NOTE: The Products Menu methods are no longer referenced by JSF page
private Product product;
private List<Product> products;
private void initProducts() {
products = dataService.getProducts();
if (products.size() > 0) product = products.get(0);
}
public List<SelectItem> getProducts() {
List<SelectItem> list = new ArrayList<SelectItem>();
if (patient != null) {
products.forEach(product -> {
if (product.getInsurance().equals(patient.getInsurance())) {
list.add(new SelectItem(product.getId(), product.getName()));
}
});
}
return list;
}
public Long getProductId() {
return product == null ? 0 : product.getId();
}
public void setProductId(Long id) {
products.forEach(product -> {
if (product.getId() == id) {
this.product = product;
}
});
}
}
Good Results
Bad Results
Log Differences 1
Log Differences 2
This issue was never solved. It was made to go away by recreating the Eclipse project from scratch; simultaneously upgrading to JSF 2.3 / CDI 1.2.

Action bean not being called

I have problem using JSF 2.0. The command button doesn't call the bean, I have already read the balusc answer commandButton/commandLink/ajax action/listener method not invoked or input value not updated but I don't think I'm encountering those cases,
this is the code I'm using:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://java.sun.com/jstl/core">
....
<ui:repeat var="skill" value="#{skillsView.skills}">
<h:form>
<h:commandButton value="#{skill}"
action="#{skillsController.removeSkillFromPublication}" ajax="true">
<f:param name="theskill" value="#{skill}" />
</h:commandButton>
</h:form>
</ui:repeat>
</ui:composition>
UPDATE 1 :
this my controller
#Controller
#Scope(value = WebApplicationContext.SCOPE_REQUEST)
public class SkillsController extends BController {
private static final Logger logger = LoggerFactory
.getLogger(SkillsController.class);
public void removeSkillFromPublication() {
logger.info("Deleting : " + getParameter("theskill"));
publicationService.removeSkillFromPublication(publicationDetailView
.getPublicationFullView().getId(), getParameter("theskill"));
skillsView.setSkills(publicationService
.getSkillsFromPublication(publicationDetailView
.getPublicationFullView().getId()));
}
}
i don't get the the logger neither the task is executed , i already tested with actionlistner with controller with actionevent and this doesent work
UPDATE 2 :
It's working with changing the SCOPE of the skillsView To WebApplicationContext.SCOPE_SESSION , anybody know why ?!!
#Controller
#Scope(value = WebApplicationContext.SCOPE_SESSION)
public class SkillsView {
List<String> skills;
private String currentSkill;
public List<String> getSkills() {
return skills;
}
public void setSkills(List<String> skills) {
this.skills = skills;
}
public String getCurrentSkill() {
return currentSkill;
}
public void setCurrentSkill(String currentSkill) {
this.currentSkill = currentSkill;
}
The h:commandButton attribute action requires you to return a String
Taken from the jsf-toolbox:
action
The action attribute accepts a method-binding expression for a backing bean action method to invoke when this component is activated by the user. An action method must be a public method with no parameters that returns a String. The returned string represents the logical outcome of the action (eg. "success", "failure", etc. ) and is used by the JavaServer Faces MVC framework to determine which view to display next.
[bolding by me]
Instead use the actionListener property:
<h:commandButton actionListener="${skillsController.removeSkillFromPublication()}" ajax="true">
<f:param name="theskill" value="#{skill}" />
</h:commandButton>
Keep in mind you will have to change the method-signature to have an ActionEvent as parameter:
public void removeSkillFromPublication(ActionEvent e) {
//...
See the toolbox:
actionListener
The actionListener attribute accepts a method-binding expression for a backing bean action listener method that will be notified when this component is activated by the user. An action listener method must be a public method with an ActionEvent parameter and a void return type.
Alternatively you could have your removeSkillFromPublication() return a String. But that seems to be the wrong approach here.

retaining values across screen sin JSF2.0

I am working on JSF2.0 project where I need to get value in backing bean from previous screen I have explained it below
my first screen
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
>
<h:body>
<h:form>
<h:outputText value="LocationID" />
<h:inputText value="#{getDetails.locationID}"/>
<h:commandButton value="Add OS" action="#{getDetails.addOSDetails}"/>
</h:form>
</h:body>
</html>
My backing bean when the commandButton is invoked
#ManagedBean(name="getDetails")
#RequestScoped
public class GetDetailsBean {
private String locationID;
public String getLocationID() {
return locationID;
}
public void setLocationID(String locationID) {
this.locationID = locationID;
}
public String addOSDetails(){
return "/app_pages/addOS";
}
}
My second screen which is addOS is
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
>
<h:body>
<h:form>
<h:outputText value="Enter Value" />
<h:inputText value="#{addOS.addValue}"/>
<h:commandButton value="Add OS" action="#{addOS.save}"/>
</h:form>
</h:body>
</html>
I want the LocationID entered in the first screen to be available in this backing bean
#ManagedBean(name="addOS")
#RequestScoped
public class AddOS {
private String addValue;
public String getAddValue() {
return addValue;
}
public void setAddValue(String addValue) {
this.addValue = addValue;
}
public String save(){
return "app_pages/success";
}
}
I donot want values to be set in session.Can be used.
Thoughts and help please
Thanks.
If you really intend to do a forward, as you do in your question, the submitted on the first page data is already there when the second page is rendered: remember, the forward is done within the same HTTP request. So, what you ultimately need is to keep the value for a subsequesnt POST request. You can do it, for example, by storing the information is a hidden field on the second page:
<h:inputHidden value="#{getDetails.locationID}" />
And that's basically it.
If you intend to do a redirect, thus by appending ?faces-redirect=true to a navigation case outcome, you need to store that information in EL flash to be able to retrieve it in the recepient page: there are two requests to be done. So, change your first bean's action method to the following:
public String action() {
...
FacesContext.getCurrentInstance().getExternalContext().getFlash().put("locationID", locationID);
return "result?faces-redirect=true";
}
This way on the recepient page it will be available in flash map, thus by
#{flash.locationID}
in EL scope and
(String)FacesContext.getCurrentInstance().getExternalContext()
.getFlash().get("locationID");
in Java code.

Primefaces commandButton calls action method on page refresh

Completely edited:
Maybe I was mixing problems and misinterpreted. After simplifying my code the question simplifies to: How can I prevent the <p:commandButton> from executing it's action method on page refresh (like when you hit F5 inside browser window)?
JSF Code:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:body>
<h:form>
<h:outputText value="#{bugBean.number}" />
<h:outputText value="#{bugBean.isComplete()}" />
<p:commandButton id="entryCommand" value="add"
action="#{bugBean.increase()}" update="#form" oncomplete="#{bugBean.complete()}"/>
</h:form>
</h:body>
</html>
backing bean code:
package huhu.main.managebean;
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
#Named
#SessionScoped
public class BugBean implements Serializable {
private static final long serialVersionUID = 1L;
private int number;
private boolean isComplete = false;
public void increase(){
number++;
}
public void complete(){
isComplete = true;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public boolean isComplete() {
return isComplete;
}
public void setComplete(boolean isComplete) {
this.isComplete = isComplete;
}
}
Update:
Even if I remove the oncomplete stuff like this an click the <p:commandButton> just once, the counter goes up on every page refresh.
<h:form>
<h:outputText value="#{bugBean.number}" />
<p:commandButton id="entryCommand" value="add"
action="#{bugBean.increase()}" update="#form"/>
</h:form>
The construct was lacking Ajax-support due to a missing head definition as it seems. In this case I just added <h:head/> right above the <h:body>-tag and everything worked fine.
Thanks to all contributors!
I think that the action method increase() is not called on each page refresh, but it's called the complete() method instead, and this is probably making you think that the action method has been called.
The oncomplete attribute inside the p:commandButton indicates a client side action, and so a JS method, and not a server action: the EL executes #{bugBean.complete()} when parses it on each page refresh.

Resources