I would like a sugestion how implement this case. (using jsf 2.0 and primefaces 3.5)
Have about 10 primefaces inputTexts
Have one primefaces dataTable
Have an entity named Contact with 10 properties like (name,description,etc...)
How the best way to add rows in dataTable after typing in the inputTexts and clicking on button. ? (I cant persist data on DB. Only add the typed new data in the dataTable new row)
thanks,
so here's what you'd do (10 times) to get your result :
Start with specifying whichever entity bean you desire, we'll stay generic here to make this useful for as many good souls :
package pack;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
#ManagedBean
#RequestScoped
public class Entity {
private String entityProperty ;
public String getEntityProperty() {
return entityProperty;
}
public void setEntityProperty(String entityProperty) {
this.entityProperty = entityProperty;
}
public Entity(String e) {
this.entityProperty = e ;
}
}
You'll have then to use this Entity in a bean (which I called Bean). We do that to fill a list which the dataTable we'll iterate to create its rows. Here's the bean :
package pack ;
import java.util.ArrayList;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
#ManagedBean
#RequestScoped
public class Bean {
private String property ;
private ArrayList<Entity> list ;
public ArrayList<Entity> getList() {
return list;
}
public void setList(ArrayList<Entity> list) {
this.list = list;
}
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
public Bean() {
list = new ArrayList<Entity>();
}
public void showInDataTable(){
list.add(new Entity(property));
}
}
Lastly, we come to the presentation page, a tour on primefaces site usually gives you an idea about what to use and how :
<?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:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>StackOverflow</title>
</h:head>
<h:body>
<h:form>
<p:inputText value="#{bean.property}" />
<p:commandButton value="show in dataTable" action="#{bean.showInDataTable}" update="dataTable"/>
<p:dataTable id="dataTable" value="#{bean.list}" var="o">
<p:column>
<h:outputText value="#{o.entityProperty}" />
</p:column>
</p:dataTable>
</h:form>
</h:body>
</html>
So you adapt this to your needs, it should flow nicely once you determine which properties your Entity equivalent must handle (that's to say, in your case, 9 more properties for the bean and for the entity and an adjusted constructor for those).
Best of luck.
Try following
Make List of Contact class as table value.
In the actionListener method of commandButton, create new Contact class and set it's property with appropriate inputText.
Append that newly created object to List
In commandButton : process inputTexts, update Datatable
Which will not required to store intermediate result into database
Related
I have a <ui:repeat> that iterates over a List<String> and creates a <p:commandButton> with the value of the current String in a <p:lightBox>.
But when I add widgetVar to my <p:lightBox>'s attributes the value of the <p:commandButton> is always the String from the last iteration.
Can someone explain what happens and (as I need widgetVar) maybe point out a solution?
This is my html:
<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:body>
<ui:repeat var="thing" value="#{bugBean.things}">
<p:lightBox widgetVar="whatever">
<h:outputLink>
<h:outputText value="#{thing}" />
</h:outputLink>
<f:facet name="inline">
<h:form>
<p:commandButton action="#{bugBean.writeThing(thing)}"
value="#{thing}" />
</h:form>
</f:facet>
</p:lightBox>
</ui:repeat>
</h:body>
</html>
This is the backing bean:
package huhu.main.managebean;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
#Named
#SessionScoped
public class BugBean implements Serializable {
private static final long serialVersionUID = 1L;
List<String> things = new ArrayList<String>();
public BugBean(){
things.add("First");
things.add("Second");
things.add("Third");
}
public void writeThing(String thing){
System.out.println(thing);
}
public List<String> getThings() {
return things;
}
public void setThings(List<String> things) {
this.things = things;
}
}
The widgetVar basically generates a window scoped JavaScript variable. What you're effectively doing now in JavaScript context is:
window['whatever'] = new Widget(lightboxElement1);
window['whatever'] = new Widget(lightboxElement2);
window['whatever'] = new Widget(lightboxElement3);
// ...
This way the whatever variable in JS would only refer the last one.
You should basically be giving them each an unique name, for example by adding the iteration index:
<ui:repeat var="thing" value="#{bugBean.things}" varStatus="iteration">
<p:lightBox widgetVar="whatever#{iteration.index}">
This way it becomes effectively:
window['whatever0'] = new Widget(lightboxElement1);
window['whatever1'] = new Widget(lightboxElement2);
window['whatever2'] = new Widget(lightboxElement3);
// ...
This way you can refer the individual lightboxes by whatever0, whatever1, whatever2, etc.
Unrelated to the concrete problem: isn't it easier to use a single lightbox and update its content on every click instead?
recently i had a similar problem when upgrading to primefaces 4.0 -> 5.1
I had to use syntax:
PF('whatever0')
Due to changes in how primefaces names the widgetvar.
My application has a query param in the url which is used to render different data using the same xhtml (all dynamic content) in a datatable.
Scope of the bean is session scope, the datatable renders all data, and has lots of input elements which open different dialogs.
If I open another browser tab sending a different query param, the datatable renders perfectly, however if I go back to the first tab, all commandlinks won't invoke the actions and the whole application will start to act very erratically.
According to my research this is because I changed the data in the datatable, I tried naming it with dynamic ids, dynamic widget names, but nothing seems to work.
<f:metadata>
<f:viewParam name="param" value="#{moduleBean.param}"/>
<f:viewAction action="#{moduleBean.setup}" />
</f:metadata>
<c:set var="module" value="#{moduleBean.param}" />
<p:dataTable id="#{module}-dataTable" value="#{moduleBean.model[module]}" var="data">
<p:commandLink action="#{moduleBean.openModuleDetails}" update=":#{module}-searchDialog" oncomplete="PF('#{module}-searchWidget').show();">
<f:param name="module" value="#{module}" />
<f:param name="dataRow" value="#{data.dbKey}" />
</p:commandLink>
</p:dataTable>
#Named
#javax.faces.view.ViewScoped
public class ModuleBean implements Serializable {
private String param;
public void setup() throws IOException {
this.model.put(this.param, new LazyDataModel(this.param));
}
public Map<String, LazyDataModel> getModel() {
return model;
}
}
This builds all the expected html with all correct ids in each tab, however JSF is still not processing the action inside the commandlink. Needless to say, if I stick to only one browser tab everything works perfectly.
Sometimes it starts working after clicking twice in the link, but going back and forth between the browser tabs will eventually always crash it.
Adding an action listener to the commandlink didnt fix it either.
Any suggestions on how to make JSF treat the same datatable as different entities on the same page but with different parameters ?
Without knowing more about the underlying bean - if you place your moduleBean in #SessionScoped this would be the expected behavior. The session (and session scoped beans) are shared between browser tabs. So you cannot rely on the underlying values from two different tabs.
Try changing to #RequestScoped/#ViewScoped for the backing values of the table data.
Here is a complete solution that works, note that this uses PrimeFaces 6.2, Apache Commons and Lombok;
#Data
#Named
#ViewScoped
public class TableTestBackingBean implements Serializable {
private int param;
#Inject
private PersonsBean personsBean;
public void onClicked() {
System.out.println("Clicked fine!");
}
public List<Person> getPersons() {
return personsBean.getPersons()[param];
}
}
#Data
#ApplicationScoped
public class PersonsBean {
#Data
#AllArgsConstructor
public class Person {
private String firstName;
private String lastName;
private int age;
}
private List<Person> persons[];
#PostConstruct void init() {
persons = new List[4];
for (int j = 0; j < 4; j++) {
persons[j] = new ArrayList<>();
for (int i = 0; i < 30; i++) {
final String firstName =
RandomStringUtils.randomAlphanumeric(10);
final String lastName =
RandomStringUtils.randomAlphanumeric(10);
final int age = RandomUtils.nextInt(0, 120);
persons[j].add(new Person(firstName, lastName, age));
}
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui" xmlns:f="http://java.sun.com/jsf/core">
<f:metadata>
<f:viewParam name="param" value="#{tableTestBackingBean.param}"/>
</f:metadata>
<h:head>
<title>Test</title>
</h:head>
<h:body>
<p:dataTable value="#{tableTestBackingBean.persons}" var="t">
<p:column headerText="First Name"><h:outputText value="#{t.firstName}" /></p:column>
<p:column headerText="Last Name"><h:outputText value="#{t.lastName}" /></p:column>
<p:column headerText="Age"><h:outputText value="#{t.age}" /></p:column>
</p:dataTable>
<h:form>
<p:commandButton action="#{tableTestBackingBean.onClicked}" value="Click Me!" />
</h:form>
</h:body>
</html>
This uses an application scoped bean for the table data, keeping it completely static. This works without a hitch and the table renders the data differently based on the parameter passed in param.
I have list in view scoped bean. The #{repeatController.add} a new object to the list. It call the h:commandButton and the p:commandButton also.
My problem is, when I have a new line, typing into the "repeatName" field something, after press h:commandButton or p:commandButton, they have different working.
The h:commandButton push the new field value to the bean and it appeared after refresh the form and the new line also.
The p:commandButton not push the new field value to the bean. After refresh the form the new line appeared, but the previously typed content of the "repeatName" field is empty.
I think the h:commandButton working fine, the p:commandButton is not working fine.
The "noRepeatName" field value always set with h:commandButton and p:commandButton.
Steps and result with h:commandButton:
push the button (create new line) more times.
type to the "noRepeatName" field
type to the the first "repeatName" field
push the button
Result: after refresh, previously typed content of the field appeared.
Steps and result with p:commandButton:
push the button (create new line) more times.
type to the "noRepeatName" field
type to the the first "repeatName" field
push the button
Result: after refresh, previously typed content of the "noRepeatName" field appeared, but the first "repeatName" is empty.
Do I miss something?
Why doesn't set the value of the first "repeatName" filed to the bean like with h:commandButton?
I use primefaces 6.2.10, jsf 2.2.18 (oracle) and spring boot 2.0.6.
JSF code
<?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:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head/>
<h:body>
<h:form id="repeatForm">
<h:commandButton value="plus" id="addJsf">
<f:ajax event="click" immediate="true" render="repeatForm" execute="repeatForm"
listener="#{repeatController.add}"/>
</h:commandButton>
<p:commandButton icon="fa fa-plus" actionListener="#{repeatController.add}" immediate="true" id="add"
process="repeatForm" update="repeatForm"/><br/>
<p:inputText value="#{repeatData.name}" id="noRepeatName">
<f:validateLength minimum="30"/>
</p:inputText><br/>
<ui:repeat value="#{repeatData.products}" var="product" varStatus="productStatus">
First: #{productStatus.first}"; Last: #{productStatus.last}; Index: #{productStatus.index}<br/>
<p:inputText value="#{product.name}" id="repeatName">
<f:validateLength minimum="20"/>
</p:inputText><br/>
</ui:repeat>
</h:form>
</h:body>
</html>
Java bean
#Named
#Scope("view)
public class RepeatData {
private List<Product> products = new ArrayList<>();
private String name;
public List<Product> getProducts() {
return products;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Product
public class Product {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Controller
#Named
#Scope("request")
public class RepeatController {
#Autowired
private RepeatData repeatData;
public void add() {
repeatData.getProducts().add(new Product());
}
}
I have a primafaces datatable like this
<pf:dataTable id="#{controller.tableComponentId}"
rows="#{controller.rowsPerPage}"
rowsPerPageTemplate="#{controller.rowsPerPageTemplate}"
<pf:ajax event="page" listener="#{controller.onPageChange}"/>
/>
My problem is that when the user changed the number of rows to be displayed. The page event is fired but with the old value of rows. So if the initial value of rows was 10, then the user changed it into 25. I still read the value 10 then JSF calls the rowsPerPage setter.
I am aware of this thread PrimeFaces dataTable: how to catch rows-per-page event? It is basically the same problem. I tried the solution mentioned here but it didnt work for me. I also use pagination but for the simplicity i didn't put it in my code.
I also tried to use process="#form" and read the request parameter map, but the "dt_rppDD" value is not sent in the ajax request. Any other suggestions how to do that ?
Getting rows in page event listener
Page:
<?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:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Datatable Page</title>
</h:head>
<h:body>
<h:form>
<p:dataTable id="#{datatablePage.tableComponentId}" rows="#{datatablePage.rows}" paginator="true"
rowsPerPageTemplate="#{datatablePage.rowsPerPageTemplate}" value="#{datatablePage.list}" var="row">
<p:ajax event="page" listener="#{datatablePage.onPageChange}"/>
<p:column>
<h:outputText value="#{row}"/>
</p:column>
</p:dataTable>
</h:form>
</h:body>
</html>
Backing bean:
package org.antonu17;
import org.omnifaces.cdi.ViewScoped;
import org.omnifaces.util.Faces;
import org.primefaces.event.data.PageEvent;
import javax.faces.component.UIComponent;
import javax.inject.Named;
import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
#Named("datatablePage")
#ViewScoped
public class DatatablePage implements Serializable {
private String tableComponentId = "table";
private Integer rows = 20;
private String rowsPerPageTemplate = "20,30,40,50";
private List<Integer> list = IntStream.range(1,200).boxed().collect(Collectors.toList());
public void onPageChange(PageEvent event) {
System.out.println(Faces.getRequestParameter(((UIComponent)event.getSource()).getClientId().concat("_rows")));
}
public String getTableComponentId() {
return tableComponentId;
}
public void setTableComponentId(String tableComponentId) {
this.tableComponentId = tableComponentId;
}
public Integer getRows() {
return rows;
}
public void setRows(Integer rows) {
this.rows = rows;
}
public String getRowsPerPageTemplate() {
return rowsPerPageTemplate;
}
public void setRowsPerPageTemplate(String rowsPerPageTemplate) {
this.rowsPerPageTemplate = rowsPerPageTemplate;
}
public List<Integer> getList() {
return list;
}
}
I found a solution to my problem but it was irrelevant to the question I asked. As i used my own implementation for PaginatorElementRenderer , JSF didn't use my class but the default one.
The question in this link is explaining my problem and solution.
How to customize Pagination in Primefaces Data Table
As i am using older version than 5.1 I had to use reflection to access the map and set my own class.
Original question is below, but as I have come up with a more minimal example to demonstrate this problem, and figured it should go at the top.
Anyway, it appears that ui:repeat tags are processed before checking to see if parent elements are actually rendered. To recreate this, here is the facelet (minimalTest.xhtml):
<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:head>
<title>Test JSF <ui:repeat> inside <h:panelGroup rendered="false"></title>
</h:head>
<h:body>
<h:form>
<h1>Testing</h1>
<h:panelGroup rendered="false">
<span>#{minimalTestBean.alsoThrowsException}</span>
<ul>
<ui:repeat value="#{minimalTestBean.throwsException}" var="item">
<li>#{item}</li>
</ui:repeat>
</ul>
</h:panelGroup>
</h:form>
</h:body>
</html>
With using this bean (MinimalTestBean.java):
package com.lucastheisen.beans;
import java.io.Serializable;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class MinimalTestBean implements Serializable {
private static final long serialVersionUID = 9045030165653014015L;
public String getAlsoThrowsException() {
throw new RuntimeException( "rendered is false so this shouldnt get called either" );
}
public List<String> getThrowsException() {
throw new RuntimeException( "rendered is false so this shouldnt get called" );
}
}
From this example you can see that the h:panelGroup that contains the ui:repeat is statically set to rendered=false which I would assume would mean that none of the EL expressions inside of that h:panelGroup would get executed. The EL expressions just call getters which throw a RuntimeException. However, the ui:repeat is actually calling the getter for its list thus causing the exception even though it should not be getting rendered in the first place. If you comment out the ui:repeat element, no exceptions get thrown (even though the other EL expression remains in the h:panelGroup) as I would expect.
Reading other questions here on stackoverflow leads me to believe that is likely related to the oft-referred-to chicken/egg issue, but I am not sure exactly why, nor what to do about it. I imagine setting the PARTIAL_STATE_SAVING to false might help, but would like to avoid the memory implications.
---- ORIGINAL QUESTION ----
Basically, I have a page that conditionally renders sections using <h:panelGroup rendered="#{modeXXX}"> wrapped around <ui:include src="pageXXX.xhtml" /> (per this answer). The problem is that if one of the pageXXX.xhtml has a <ui:repeat> inside of it, it seems to get processed even when the containing <h:panelGroup> has rendered=false. This is a problem because some of my sections rely on having been initialized by other sections that should be visited before them. Why is the included pageXXX.xhtml getting processed?
This is a painful bug and incredibly hard to boil down to a small example, but here is the most minimal case I could build that demonstrates the issue. First a base page:
<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:head>
<title>Test JSF <ui:include></title>
</h:head>
<h:body>
<h:form>
<h1>#{testBean.title}</h1>
<h:panelGroup rendered="#{testBean.modeOne}">
<ui:include src="modeOne.xhtml" />
</h:panelGroup>
<h:panelGroup rendered="#{testBean.modeTwo}">
<ui:include src="modeTwo.xhtml" />
</h:panelGroup>
</h:form>
</h:body>
</html>
As you can see this page will conditionally include either the modeOne page or the modeTwo page based upon the value in the testBean bean. Then you have modeOne (the default):
<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">
<ui:composition>
<span>Okay, I'm ready. Take me to </span>
<h:commandLink action="#{testBean.setModeTwo}">mode two.</h:commandLink>
</ui:composition>
</html>
Which in my real world app would be a page that sets up things needed by modeTwo. Once set up, an action on this page will direct you to modeTwo:
<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">
<ui:composition>
<div>Here is your list:</div>
<ui:repeat value="#{testBeanToo.list}" var="item">
<div>#{item}</div>
</ui:repeat>
</ui:composition>
</html>
The modeTwo page basically presents a details for the modeOne page in a ui:repeat as the actual information is in a collection. The main managed bean (TestBean):
package test.lucastheisen.beans;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class TestBean implements Serializable {
private static final long serialVersionUID = 6542086191355916513L;
private Mode mode;
#ManagedProperty( value="#{testBeanToo}" )
private TestBeanToo testBeanToo;
public TestBean() {
System.out.println( "constructing TestBean" );
setModeOne();
}
public String getTitle() {
System.out.println( "\ttb.getTitle()" );
return mode.getTitle();
}
public boolean isModeOne() {
return mode == Mode.One;
}
public boolean isModeTwo() {
return mode == Mode.Two;
}
public void setModeOne() {
this.mode = Mode.One;
}
public void setModeTwo() {
testBeanToo.getReadyCauseHereICome();
this.mode = Mode.Two;
}
public void setTestBeanToo( TestBeanToo testBeanToo ) {
this.testBeanToo = testBeanToo;
}
private enum Mode {
One("Mode One"),
Two("Mode Two");
private String title;
private Mode( String title ) {
this.title = title;
}
public String getTitle() {
return title;
}
}
}
Is the bean for all the main data, and the TestBeanToo bean would be for the details:
package test.lucastheisen.beans;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class TestBeanToo implements Serializable {
private static final long serialVersionUID = 6542086191355916513L;
private ObjectWithList objectWithList = null;
public TestBeanToo() {
System.out.println( "constructing TestBeanToo" );
}
public String getTitle() {
System.out.println( "\ttb2.getTitle()" );
return "Test Too";
}
public List<String> getList() {
System.out.println( "\ttb2.getList()" );
return objectWithList.getList();
}
public void getReadyCauseHereICome() {
System.out.println( "\ttb2.getList()" );
objectWithList = new ObjectWithList();
}
public class ObjectWithList {
private List<String> list;
public ObjectWithList() {
list = new ArrayList<String>();
list.add( "List item 1" );
list.add( "List item 2" );
}
public List<String> getList() {
return list;
}
}
}
<ui:repeat> does not check the rendered attribute of itself (it has actually none) and its parents when the view is to be rendered. Consider using Tomahawk's <t:dataList> instead.