How to reuse Primefaces datatable with different data in different browser tabs? - jsf

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.

Related

p:autocomplete itemSelect and change ajax events incosistency, solution without widgetVar [duplicate]

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.

primefaces commandButton not set input field value in ui:repeat

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());
}
}

How to call a managed bean method from an outputLink?

This may have been covered somewhere but I'm having trouble forming the question for search engine and no goods leads thus far.
I'm working on a page that acts as entity view. Lots of results come from database and only a handful are displayed at a time. So you can imagine that I want to build a list of links that take user to another page of entities. This is all my code - no PrimeFaces or any other front-end nifty pagination solutions. At least for now.
To the code:
#Named
#SessionScoped
public class ArticleIndexBean {
List<Article> articleList=new ArrayList<>();
List<Article> articleSubList=new ArrayList<>();
#PostConstruct
public void loadScreenSupport() {
search();
toEntityPage(1);
}
protected void search() {
// this method sets articleList which is the full list fetched from the database
}
public void toEntityPage(int pageNumber) {
// this method sets articleSubList which is a subset of articleList
}
Each page link needs to call toEntiyPage(n). I am aware of commandLink but I want to avoid a POST request. Also, the bean is currently session scoped and I will try to make it conversation scoped later. It will certainly NOT be request scoped, as I don't want to do a full db search each time a user wants to jump to another page. So #PostConstruct won't help, either.
So with a menu like this: 1 * 2 * 3 * 4 * 5 how do I code an outputLink or any other type of link that will call my ArticleIndexBean.toEntityPage(int) via a GET request?
Solution
Based on input from Laurent, I added a currentEntityPageNumber property and a toCurrentEntityPage() method to my bean. The toCurrentEntityPage() simply calls toEntityPage(getCurrentEntityPageNumber()).
<html lang="en"
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"
>
<f:metadata>
<f:viewParam name="pn" value="#{articleIndexBean.currentEntityPageNumber}" />
<f:event type="preRenderView" listener="#{articleIndexBean.toCurrentEntityPage()}" />
</f:metadata>
<c:forEach var="pageNumber" begin="1" end="${articleIndexBean.getEntityPageCount()}">
<h:outputLink value="ar_index.xhtml">
<h:outputText value="${pageNumber}" />
<f:param name="pn" value="${pageNumber}" />
</h:outputLink>
</c:forEach>
It would certainly be better if we could call toEntityPage(pageNumber) directly but this works fine.
Assuming you are using JSF 2.2, you could use the viewParam to retrieve the page in the GET parameters and viewAction to call a method before the rendering (actually called in the INVOKE_APPLICATION phase by default).
Facelets:
<f:metadata>
<f:viewParam name="page" value="#{articleIndexBean.entityPage}" />
<f:viewAction action="#{articleIndexBean.loadScreenSupport}" />
</f:metadata>
If you are using JSF 2.0 or JSF 2.1, then you have to replace viewAction by:
<f:event type="preRenderView" listener="#{articleIndexBean.loadScreenSupport}" />
Java:
#Named
#SessionScoped
public class ArticleIndexBean {
List<Article> articleList=new ArrayList<>();
List<Article> articleSubList=new ArrayList<>();
int pageNumber = 1; // by default first page
public void loadScreenSupport() {
search();
toEntityPage(pageNumber);
}
public int getPageNumber() {
return pageNumber;
}
public void setPageNumber(int pageNumber) {
this.pageNumber = pageNumber;
}
protected void search() {
// this method sets articleList which is the full list fetched from the database
}
public void toEntityPage(int pageNumber) {
// this method sets articleSubList which is a subset of articleList
}
}
The link to the page is then easy:
<h:outputLink value="resultPage.xhtml">
<h:outputText value="2" />
<f:param name="page" value="2" />
</h:outputLink>
Reference:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?

how to add a component to the page from a managed bean in jsf / primefaces [duplicate]

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

Insert primefaces dataTable rows based on p:inputText fields

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

Resources