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.
Related
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.
This question already has answers here:
How to dynamically add JSF components
(3 answers)
Closed 6 years ago.
A click on a commandButton should trigger an action in a ManagedBean: to add a new "outputText" component to the current page.
The overall idea is to have the page changed dynamically with user action, with server side action because new elements added to the page need data from a db to be laid out.
-> How do I add a component to the page from a managed bean in jsf / primefaces? Let's say that the elements should be added in an existing div like:
<div id="placeHolder">
</div>
(this div could be changed to a jsf panel if needs be)
Note: if alternative methods are better to achieve the same effect I'd be glad to learn about them.
I'll provide you another solution apart from the one you posted. Basically it has a List of given outputs, which is increased everytime the button is pushed. That should render exactly the same DOM tree as the solution you stated:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>Tiles</title>
<h:outputStylesheet name="css/320andup_cle.css" />
</h:head>
<h:body>
<h:form>
<h:commandButton actionListener="#{bean.createNewTile}" title="new"
value="new" />
</h:form>
<h:panelGroup layout="block" id="tiles">
<ui:repeat var="str" value="#{bean.strings}">
<h:panelGroup>
<h:outputText styleClass="tile" value="#{str}" />
</h:panelGroup>
</ui:repeat>
</h:panelGroup>
</h:body>
</html>
#ManagedBean
#SessionScoped
public class Bean {
List<String> strings = new ArrayList<String>();
public List<String> getStrings() {
return strings;
}
public void createNewTile() {
strings.add("output");
}
}
Apart from being much simpler IMHO, it has a main advantage: it doesn't couple your server side code to JSF implicit API. You can change the #ManagedBean annotation for #Named if you want it to be a CDI managed bean.
The solution:
This is a jsf page with a button creating a new div each time it is clicked:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>Tiles</title>
<h:outputStylesheet name="css/320andup_cle.css" />
</h:head>
<h:body>
<h:form>
<h:commandButton actionListener="#{bean.createNewTile()}" title="new" value="new"/>
</h:form>
<h:panelGroup layout="block" id="tiles">
</h:panelGroup>
</h:body>
</html>
The Managed Bean:
#Named
#SessionScoped
public class Bean implements Serializable {
private UIComponent found;
public void createNewTile() {
HtmlPanelGroup div = new HtmlPanelGroup();
div.setLayout("block");
HtmlOutputText tile = new HtmlOutputText();
tile.setValue("heeeeeRRRRRRRRRRRRRR ");
tile.setStyleClass("tile");
div.getChildren().add(tile);
doFind(FacesContext.getCurrentInstance(), "tiles");
found.getChildren().add(div);
}
private void doFind(FacesContext context, String clientId) {
FacesContext.getCurrentInstance().getViewRoot().invokeOnComponent(context, clientId, new ContextCallback() {
#Override
public void invokeContextCallback(FacesContext context,
UIComponent component) {
found = component;
}
});
}
}
See this app built with this logic of dynamically generated components: https://github.com/seinecle/Tiles
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
I'm experiencing strange behaviour with my session scoped bean. I used following imports and annotations to make it sessionscoped:
EDIT : more Code
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
#Named
#SessionScoped
public class DetailsBean implements Serializable {
private LinkedHashMap<String, String> folder;
#Inject
private ApplicationBean appBean;
#Inject
private UserBean userBean;
#PostConstruct
public void resolveID() {
this.folder = new LinkedHashMap<String, String>();
for (LinkedHashMap<String, String> tempfolder : appBean.getRepositoryContent()) {
if (tempfolder.get("text:nodeid").equals(URLid)) {
this.folder = tempfolder;
}
}
}
Code Snippet of JSF page :
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:rich="http://richfaces.org/rich">
<f:metadata>
<f:viewParam name="id" value="#{detailsBean.URLid}" required="true" requiredMessage="You must provide an Object Id"/>
<f:event type="preRenderView" listener="#{detailsBean.resolveID}" />
</f:metadata>
<h:head>
<title>Dataset #{detailsBean.name}</title>
</h:head>
<h:body>
<h:form>
<h:panelGrid columns="2" columnClasses="fixed-column">
Name <h:inputText value="#{detailsBean.name}"
id="name" required="true"
requiredMessage="name required"/>
<rich:message for="name" ajaxRendered="true"/>
</h:panelGrid>
</h:body>
</h:form>
</html>
Now when I click on a link in my jsf page such a DetailsBean gets instantiated. When I click on another link with different content the same bean is used because I am still within the same Session. Now the strange thing is that even though I created 2 different browser tabs they show different content even after refreshing the page. How can the same bean instance show different contents ? I thought normally only a #ViewScoped bean could achieve this ? Don't get me wrong I DO want them to show different content so #ViewScoped would be the right decision to use here but I just wonder how this is possible...
EDIT2 : When I use javax.faces.ViewScoped, above Code doesn't work anymore (I get java.io.NotSerializableException because of the LinkedHashMap then)
I believe you have error in import line. import javax.enterprise.context.SessionScoped;. You should import annotations from javax.faces.bean.SessionScoped.
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.