I am working on JSF2.0 project where a screen has got 60 fields with prepopulated values.
User can edit any number of fields in the screen and I have to display the user edited fields with old and new values in the next screen when he clicks on 'Save' button.
What would be the best way to do that .
Screen should be this way
FieldName Old Value New Value
Any help appreciated
Thanks.
Let's suppose that your prepopulated values have pulled from database in some object, called someObject which is a field of SomeBean, and displayed like
<h:inputText value="#{someBean.someObject.someValue}">.
After someObject has been populated from database, you can make another (copy) object: oldSomeObject, to which you copy the field values of the someObject. After submitting form with 60 values, on new page, you can call method from SomeBean:
public ArrayList<EditedField> getEditedFields(){
ArrayList<EditedField> editedFields = new ArrayList<EditedField>();
for(int i=0; i<oldSomeObject.getFieldValues().size(); i++){
if(!someObject.getFieldValues().contains(oldSomeObject.getFieldValues().get(i))){
editedFields.add(new EditedField(oldSomeObject.getFieldNames.get(i), oldSomeObject.getFieldValues().get(i), someObject.getFieldValues().get(i)));
}
}
return editedFields;
}
Here:
getFieldValues() is method in class SomeObject, which returns ArrayList of (String) values of all fields,
getFieldNames() is method in class SomeObject, which returns ArrayList of names of all fields (same order),
EditedField is a class which describe edited field:
public class EditedField(){
private String fieldName;
private String oldValue;
private String newValue;
public EditedField(String fieldName, String oldValue, String newValue){
this.fieldName = fieldName;
this.oldValue = oldValue;
this.newValue = newValue;
}
public void setFieldName(String fieldName){
this.fieldName = fieldName;
}
public String getFieldName(){
return fieldName;
}
public void setOldValue(String oldValue){
this.oldValue= oldValue;
}
public String getOldValue(){
return oldValue;
}
public void setNewValue(String newValue){
this.newValue= newValue;
}
public String getNewValue(){
return newValue;
}
}
Page that displays edited fields in table, should look like this:
<!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://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:body>
<h:dataTable value="#{someBean.editedFields}" var="field">
<h:column>
<f:facet name="header">
<h:outputText value="Field Name"></h:outputText>
</f:facet>
<h:outputText value="#{field.fieldName}"></h:outputText>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Old Value"></h:outputText>
</f:facet>
<h:outputText value="#{field.oldValue}"></h:outputText>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="New Value"></h:outputText>
</f:facet>
<h:outputText value="#{field.newValue}"></h:outputText>
</h:column>
</h:body>
</html>
I hope it will help.
Related
I have 3 managebean one model and 2 controllers (1 for the popup text) i want a popup after i insert into the database.
my page is like this the problem is that when i call for mensaje.mostrar() i works but when i try to call cursoControlador.registrarCurso() and from there try to call mensaje.mostrar() that changes the value from mostrarMensaje (boolean) don't show the popup.
<ui:define name="centro">
<h:form>
<h:panelGrid columns="3" cellpadding="5">
<p:outputLabel value="Nombre" />
<p:inputText value="#{cursoModelo.nombre}"/>
this is the button use and works fine
<p:commandButton action="#{mesaje.mostrar()}" value="Registrar Curso" >
<f:ajax render="#form"/>
</p:commandButton>
and this is the behavior i want
<p:commandButton action="#{cursoControlador.registrarCurso()}" value="Registrar Curso" >
<f:ajax render="#form"/>
</p:commandButton>
and this is the popup message i want to show
</h:panelGrid>
<h:panelGroup layout="block" styleClass="fondo-popup" rendered="#{mensaje.mostrarMensaje}">
<div class="panel-popup">
<p:outputLabel value="#{mensaje.texto}"/>
<h:commandButton value="Aceptar" action="#{mensaje.esconder()}">
<f:ajax render="#form"/>
</h:commandButton>
</div>
</h:panelGroup>
</h:form>
</ui:define>
model
#Named(value = "cursoModelo")
#SessionScoped
public class CursoModelo implements Serializable {
private String nombre;
//getters and setters...
}
controller 1
#Named(value = "cursoControlador")
#SessionScoped
public class CursoControlador implements Serializable{
#Inject private CursoModelo cursoModelo;
#Inject private Mensaje mensaje;
/**
* Creates a new instance of CursoControlador
*/
public CursoControlador() {
}
public String registrarCurso(){
//SOME CODE that inserts into de database
mensaje.mostrar(); //this is the problem probably i'm doing it wrong
return "index";
}
}
controller 2 (The controller for the message)
#Named(value = "mensaje")
#SessionScoped
public class Mensaje implements Serializable {
private String texto;
private boolean mostrarMensaje;
/**
* Creates a new instance of Mensaje
*/
public Mensaje() {
texto = "Mensaje a mostrar";
mostrarMensaje = false;
}
public void mostrar(){
mostrarMensaje = true;
}
public void esconder(){
mostrarMensaje = false;
}
//getters and setters from texto and mostrarMensaje
}
The reason why the topic has "kind of" is because I have an example in JSF 2.2 where I use a commandButton and call a bean function twice (depending on the url). It's basically the same code, which executes only in one example.
Here's the code with the description of the "error" below the code:
User bean
#ManagedBean
#RequestScoped
public class User {
private String name;
private String surname;
private int age;
private int id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public User(String name, String surname, int age, int id) {
super();
this.name = name;
this.surname = surname;
this.age = age;
this.id = id;
}
public User(){}
}
UsersBean bean:
#ManagedBean
#SessionScoped
public class UsersBean {
private List<User> listOfUsers = new ArrayList<User>();
private String passedParameter;
public UsersBean() {
listOfUsers.add(new User("Tywin", "Lannister", 60, 1));
listOfUsers.add(new User("Tyrion", "Lannister", 30, 2));
listOfUsers.add(new User("Jaime", "Lannister", 31, 3));
listOfUsers.add(new User("Cercei", "Lannister", 29, 4));
listOfUsers.add(new User("John", "Snow", 31, 5));
}
public List<User> getAll() {
System.out.println("getAall is called.");
return listOfUsers;
}
public User getDetails() {
passedParameter = (String) FacesContext.getCurrentInstance()
.getExternalContext().getRequestParameterMap().get("userID");
int id = Integer.parseInt(passedParameter);
User selected = null;
for (User u : listOfUsers) {
if (u.getId() == id) {
selected = u;
}
}
return selected;
}
public String addUser(User u) {
System.out.println("addUser is called.");
if (u.getId() != 0) {
for (User edit : listOfUsers) {
if (edit.getId() == u.getId()) {
System.out.println("Found it!");
edit.setAge(u.getAge());
edit.setName(u.getName());
edit.setSurname(u.getSurname());
}
}
} else {
u.setId(listOfUsers.size() + 1);
listOfUsers.add(u);
}
return "";
}
}
users.xhtml:
<f:view>
<!-- http://stackoverflow.com/questions/8083469/method-must-have-signature-string-method-etc-but-has-signature-void -->
<h:dataTable value="#{usersBean.all}" var="u">
<h:column>
<f:facet name="header">
User ID
</f:facet>
#{u.id}
</h:column>
<h:column>
<f:facet name="header">
Name
</f:facet>
#{u.name}
</h:column>
<h:column>
<f:facet name="header">
Details
</f:facet>
<h:link outcome="users" value="edit user">
<f:param name="userID" value="#{u.id}"></f:param>
<f:param name="action" value="edit"></f:param>
</h:link>
<h:link outcome="usersDetails" value="get details">
<f:param name="userID" value="#{u.id}"></f:param>
</h:link>
</h:column>
</h:dataTable>
<h:panelGroup rendered="#{param['action'] == 'edit'}">
<h1>Edit!</h1>
<h:form>
<ui:param name="editUser" value="#{usersBean.details}"></ui:param>
<h:outputText value="Name"></h:outputText>
<h:inputText value="#{editUser.name}"></h:inputText> <br />
<h:outputText value="Surname"></h:outputText>
<h:inputText value="#{editUser.surname}"></h:inputText> <br />
<h:outputText value="Age"></h:outputText>
<h:inputText value="#{editUser.age}"></h:inputText> <br />
<h:commandButton action="#{usersBean.addUser(editUser)}" value="Edit" type="submit"> </h:commandButton>
</h:form>
</h:panelGroup>
<h:panelGroup rendered="#{empty param['action']}">
<h1>Add!</h1>
<h:form>
<h:outputText value="Name"></h:outputText>
<h:inputText value="#{user.name}"></h:inputText>
<h:outputText value="Surname"></h:outputText>
<h:inputText value="#{user.surname}"></h:inputText>
<h:outputText value="Age"></h:outputText>
<h:inputText value="#{user.age}"></h:inputText>
<h:commandButton action="#{usersBean.addUser(user)}" value="Add" type="submit"></h:commandButton>
</h:form>
</h:panelGroup>
</f:view>
OK, so, everything works perfectly. usersBean.addUser adds the user. If I add another inputText for ID and I put in an existing ID, the corresponding function updates the values. So, addUser function works as expected.
The problem is in case of
<h:panelGroup rendered="#{param['action'] == 'edit'}">
as you can see in the xhtml above, the code is basically the same, with the sole exception that I fill in the data of the user that was selected. This works, I get the appropriate data into input fields, but when I change them and click Edit, nothing happens. The function is not called! whereas in case of add, the function is called and it works.
It appears as if there is no action defined in case of edit, it only reloads the page (submit) without the actual action addUser.
How is this caused and how can I solve it?
It's because #{param['action'] == 'edit'} didn't evaluate true during processing the form submit and therefore the <h:panelGroup> is basically not rendered during processing the form submit, including the <h:commandButton> sitting in there. The form submit namely did not pass that parameter back to JSF. This way JSF won't see the <h:commandButton> and therefore never be able to decode and queue its action event. You need to make sure that all your rendered conditions evaluate the same during processing the form submit as they were during displaying the form (this is part of JSF's safeguard against tampered/hacked requests wherein the enduser would otherwise be able to execute unrendered command buttons like admin-only buttons).
So, you basically need to retain that parameter during the postbacks as well so that the rendered expression still evaluates true during processing the form submit. This can be achieved in several ways:
Add it as <f:param> inside the <h:commandButton> of relevance:
<h:commandButton action="#{usersBean.addUser(editUser)}" value="Edit" type="submit">
<f:param name="action" value="#{param.action}" />
</h:commandButton>
(note: param['action'] and param.action are equivalent; those brackets are only useful if the 'action' contains periods, or represents another variable)
Set it as a bean property by <f:viewParam>. Requirement is that the bean needs to be in view scope or broader. You've it in the session scope already, which is okay (but which is IMO way too broad, but that aside):
<f:metadata>
<f:viewParam name="action" value="#{usersBean.action}" />
</f:metadata>
...
<h:panelGroup rendered="#{usersBean.action}">
...
</h:panelGroup>
If you're already using JSF utility library OmniFaces, use its <o:form> instead of <h:form> which enables you submit to the current request URI instead of to the current JSF view ID. This way the GET request string will automagically be retained and you don't need to copypaste <f:param> over all command buttons like as in #1:
<o:form useRequestURI="true">
...
</o:form>
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not set/updated - point 6
I got stuck with a problem which seems to have been solved here.
I want to use order list as it's in an example here.
But it doesn't work at all. I have nested <p:ajax> in order list like in an example.
Namely, I have got an error:
javax.servlet.ServletException: /resources/abc/rankingAnswer.xhtml #21,85<p:ajax> Unable to attach behavior to non-ClientBehaviorHolder parent
My Primefaces config in pom.xml is
<!-- prime faces -->
<primefaces.version>5.1</primefaces.version>
<primefaces.themes.version>1.0.10</primefaces.themes.version>
My view is
<?xml version="1.0" encoding="UTF-8"?>
<!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:composite="http://java.sun.com/jsf/composite"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html">
<composite:interface>
<composite:attribute name="question" />
</composite:interface>
<composite:implementation>
<ui:decorate template="answerDecorator.xhtml">
<ui:define name="component">
<p:orderList value="#{cc.attrs.question.possibleAnswers}" var="answer" itemLabel="#{answer.text}" itemValue="#{answer}" controlsLocation="left" editable="true" >
<f:facet name="caption">#{msg['survey.default.makeOrder']}</f:facet>
<p:ajax event="reorder" listener="#{cc.attrs.question.onReorder}" />
<p:column>
<h:outputText value="#{answer.text}" />
</p:column>
</p:orderList>
</ui:define>
</ui:decorate>
</composite:implementation>
</html>
My model is
public class RankingQuestionDTO extends AbstractQuestionDTO implements Serializable {
private ArrayList<RankingAnswerDTO> possibleAnswers;
private boolean randomizeAnswers;
private String text;
public RankingQuestionDTO() {
super(QuestionType.RANKING);
this.text = MessageUtils.getBundle("survey.default.text");
this.possibleAnswers = new ArrayList<>();
this.possibleAnswers.add(new RankingAnswerDTO(MessageUtils.getBundle("survey.default.text")));
this.possibleAnswers.add(new RankingAnswerDTO(MessageUtils.getBundle("survey.default.text")));
this.randomizeAnswers = true;
}
public void onSelect(SelectEvent event) {
System.out.print(event.getObject().toString());
}
public void onUnselect(UnselectEvent event) {
System.out.print(event.getObject().toString());
}
public void onReorder() {
}
public void addEmptyPossibleAnswer()
{
this.possibleAnswers.add(new RankingAnswerDTO(MessageUtils.getBundle("survey.default.text")));
}
public ArrayList<RankingAnswerDTO> getPossibleAnswers() {
return possibleAnswers;
}
public String getText() {
return text;
}
public void setPossibleAnswers(ArrayList<RankingAnswerDTO> possibleAnswers) {
this.possibleAnswers = possibleAnswers;
}
public void setRandomizeAnswers(boolean randomizeAnswers) {
this.randomizeAnswers = randomizeAnswers;
}
public boolean getRandomizeAnswers() {
return randomizeAnswers;
}
public void setText(String text) {
this.text = text;
}
}
The PrimeFaces Showcase has the answer (emphasis mine):
Pojo Support with Clip Effect, Captions, Custom Content, Reorder Controls and Events (since v5.1.5)
As does issue 4501 (emphasis mine):
Added
select
unselect
reorder
Target is 5.1.5 Elite and 5.2 Community.
I have the following composite component:
<!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:h="http://java.sun.com/jsf/html"
xmlns:composite="http://java.sun.com/jsf/composite"
>
<composite:interface>
<composite:attribute name="value"/>
<composite:editableValueHolder name="value" targets="#{cc.clientId}:value"/>
</composite:interface>
<composite:implementation>
<h:inputText id="value" />
<h:message for="#{cc.clientId}:value"/>
</composite:implementation>
</html>
And the following managed bean
import java.util.Date;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.validation.constraints.NotNull;
#ViewScoped
#ManagedBean
public class TestMB {
#NotNull
private Date value;
public String action1() {
System.out.println("The value is: " + value);
return null;
}
public Date getValue() {
return value;
}
public void setValue(Date value) {
this.value = value;
}
}
And the following custom converter:
import java.util.Date;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
#FacesConverter("myCustomConverter")
public class MyCustomConverter implements Converter {
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String value) {
if (value == null || "".equals(value)) {
return null;
}
return new Date();
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object obj) {
if (obj == null) {
return null;
}
return obj.toString();
}
}
And the following test page:
<h:body>
<h:form>
Enter value:
<wui:test value="#{testMB.value}" id="myID">
<f:converter converterId="myCustomConverter" for="value"/>
</wui:test>
<h:commandLink action="#{testMB.action1}" value="submit"/>
</h:form>
</h:body>
When I run the web application and leave the text field empty and hit the submit button no validation errors are raised and I get the following on the Java console:
The value is: null
To make things more complicated, If I modify my composite component as follows (I added the value="#{cc.attrs.value}" to the h:inputText
<!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:h="http://java.sun.com/jsf/html"
xmlns:composite="http://java.sun.com/jsf/composite"
>
<composite:interface>
<composite:attribute name="value"/>
<composite:editableValueHolder name="value" targets="#{cc.clientId}:value"/>
</composite:interface>
<composite:implementation>
<h:inputText id="value" value="#{cc.attrs.value}"/>
<h:message for="#{cc.clientId}:value"/>
</composite:implementation>
</html>
And try again to submit the form when NO value is set, I don't get any validation errors displayed on the page, but I get the following warning in the console:
INFO: WARNING: FacesMessage(s) have been enqueued, but may not have been displayed.
sourceId=myID:value[severity=(ERROR 2), summary=(may not be null), detail=(may not be null)]
Then if I try to re-submit the form again the action is submitted even if the field is empty. And I get the following in the console:
The value is: null
However if I remove the id="myID" everything is OK!
Finally: If I set prependId="false" on my form, everything is OK.
But I don't want to set prependId="false" and I do need to set an ID :(
I have a page with a "rich:DataTable". For each table row, added a checkbox to allow the user to select more than one line before excute an operation. How to retrieve the rows that were selected?
Just add a boolean , maybe called selected , to the POJO that represents a row for the rich:DataTable . And bind this boolean to the <h:selectBooleanCheckbox> in the <rich:column> inside the <rich:dataTable>
For example , your beans , POJO and the view may look likes this:
<rich:dataTable value="#{myBean.customerList}" var="customer">
<rich:column>
<h:selectBooleanCheckbox value="#{customer.selected}" />
</rich:column>
<rich:column>
<h:outputText value="#{customer.name}" />
</rich:column>
<rich:column>
<h:outputText value="#{customer.address}" />
</rich:column>
</rich:dataTable>
public class MyBean {
private List<Customer> customerList;
//getter and setter for the customerList
}
public class Customer{
private boolean selected;
private String name;
private String address;
//getter and setter for the properties
}
To retrieve the rows that were selected , just iterate the MyBean.customerList and check if the selected property of the Customer is true.
[SOLVED] I decided otherwise. Added a "Map " and a column with "CheckBox" this way: "". By submitting the form, the lines of "Map" that are selected will get the Boolean property equal "true".
My Bean:
public class Bean<T extends Object> {
private Map<T, Boolean> selectedRowsMap = new HashMap<T, Boolean>(0);
...
public Set<T> getSelectedRows() {
selectedRows.clear();
for (T key : getSelectedRowsMap().keySet()) {
if (getSelectedRowsMap().get(key) == true){
selectedRows.add(key);
}
}
return selectedRows;
}
}
My XHTML:
<rich:dataTable>
<rich:column>
<h:selectBooleanCheckbox value="#{bean.selectedRowsMap[row]}" />
</rich:column>
<rich:column>
<h:outputText value="${row.age}" />
</rich:column>
...
<rich:dataTable>