Refreshing model data on tab change event - primefaces

I'm using primefaces tabView component. Each tag has its own managed bean and .xhtml page referencing that bean.
The problem is that EACH TIME tab is clicked, fresh data should be fetched from database (not just the first time), in other words data should be refreshed.
My beans are #ViewScoped. I don't want to use #RequestScoped because I have very dynamic ajax-enabled pages.
My solution currently looks like this:
I have managed bean for the main page with tabView component (MainBean) which contains method for handling tabChange event.
That bean also has references to beans assigned to each tab (TabBean1, TabBean2) so that right bean could be refreshed according to the id of the chosen tab.
MainBean:
#ManagedBean(name = "mainBean")
#ViewScoped
public class MainBean implements Serializable {
private static final long serialVersionUID = 1L;
#ManagedProperty(value = "#{tabBean1}")
private TabBean1 tabBean1;
#ManagedProperty(value = "#{tabBean2}")
private TabBean2 tabBean2;
public void onTabChange(TabChangeEvent event) {
String id = event.getTab().getId();
if ("tab1Id".equals(id)) {
tabBean1.init();
} else if ("tab2Id".equals(id)) {
tabBean2.init();
}
}
public TabBean1 getTabBean1() {
return tabBean1;
}
public void setTabBean1(TabBean1 tabBean1) {
this.tabBean1 = tabBean1;
}
public TabBean2 getTabBean2() {
return tabBean2;
}
public void setTabBean2(TabBean2 tabBean2) {
this.tabBean2 = tabBean2;
}
}
TabBean1:
#ManagedBean(name = "tabBean1")
#ViewScoped
public class TabBean1 implements Serializable {
private static final long serialVersionUID = 1L;
private List<String> data;
#PostConstruct
public void init() {
// refresh data
}
}
TabBean2:
#ManagedBean(name = "tabBean2")
#ViewScoped
public class TabBean2 implements Serializable {
private static final long serialVersionUID = 1L;
private List<String> data;
#PostConstruct
public void init() {
// refresh data
}
}
Relevant part od main.xhtml:
<p:tabView id="tabViewID" cache="false" dynamic="true">
<p:ajax event="tabChange" listener="#{mainBean.onTabChange}" />
<p:tab title="Tab 1" id="tab1Id">
<ui:include src="tab1Page.xhtml" />
</p:tab>
<p:tab title="Tab 2" id="tab2Id">
<ui:include src="tab2Page.xhtml" />
</p:tab>
</p:tabView>
Problem with this approach is:
when MainBean is constructed, all its managed properties are constructed also. This is a huge problem because #PostConstruct method of each bean fetches some data from database and everything is just too slow beacuse all beans are initialized when only one should really be initialized (the one assigned to selected tab)
on first page load, init() method is called twice for TabBean1, once because of #PostConstruct and once because it is assigned to tabChange event (first tab is selected by default)
My question is:
how can I 'refresh bean data' when corresponding tab is clicked?
how can I do that dynamically, just for the chosen tab (initially and every click after)?
I would really appreciate your suggestions. Thanks!
I'm using Primefaces 6.0, JSF 2.2.

Related

Primefaces DataTable CellEdit not updating outputLabel

I've actually found quite a bit on this in various different forums, but none of them appear to have worked for me. My problem isn't even that complicated, but I can't figure out why it's not working. I've set up my datatable so that it's editable and set the editMode="cell". I set up the ajax event and it fires, but the new and old values are always null.
When I'm finished editing a cell and go to the next cell, the cell goes back to the old value. When I click back into it, it shows the new value. Both the outputLabel and inputText tags use the exact same variable, but they are not showing the same value. I've tried manually updating the table manually through the method call as well and that does nothing. Also tried putting a p:ajax tag inside the inputText tag to update the table and that didn't work either.
Here's my JSF code:
<p:dataTable id="dynamicTable" value="#{column.values}" var="value" editable="true" editMode="cell">
<p:ajax event="cellEdit" listener="bean.updateCell" immediate="true" update=":dynamicTable"/>
<p:columns value="#{bean.columns}" var="column">
<f:facet name="header">
<p:outputLabel value="#{column.name}"/>
</f:facet>
<p:cellEditor>
<f:facet name="output"><p:outputLabel value="#{value}"/></f:facet>
<f:facet name="input"><p:inputText value="#{value}"/></f:facet>
</p:cellEditor>
</p:columns>
</p:dataTable>
Here's my bean:
#ManagedBean(name = "bean")
#ViewScoped
public class Bean implements Serializable {
private ArrayList<Column> columns;
public ArrayList<Column> getColumns(){
return columns;
}
public void setColumns(ArrayList<Column> columns){
this.columns = columns;
}
#PostConstruct
void init(){
columns = new ArrayList<>();
Column column = new Column();
column.setName("Acronym");
ArrayList<String> values = new ArrayList<>();
values.add("KFC");
values.add("TTYL");
values.add("BRB");
column.setValues(values);
columns.add(column);
column = new Column();
column.setName("Meaning");
values = new ArrayList<>();
values.add("Kentuky Fried Chicken");
values.add("Talk to you later");
values.add("Be right back");
column.setValues(values);
columns.add(column);
}
public void updateCell(CellEditEvent event){
System.out.println((String) event.getNewValue();
}
}
And my Column class:
public class Column{
private ArrayList<String> values;
private String name;
public ArrayList<String> getValues(){
return values;
}
public void setValues(ArrayList<String> values){
this.values = values;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
I rewrote the code to not include sensitive information and renamed variables, so if I'm missing something it's possible that I already have it in my code, but have accidentally removed it.
It turns out the way I was doing the p:columns tag was incorrect. After I made the following changes my problem was solved.
I've made an additional class called TableRow with various String objects to signify the columns to be used. Here's the new code:
JSF:
<p:dataTable id="dynamicTable" value="#{bean.tblRows}" var="row" editable="true" editMode="cell">
<p:columns value="#{bean.columns}" var="column">
<f:facet name="header">
<p:outputLabel value="#{column.name}"/>
</f:facet>
<p:cellEditor>
<f:facet name="output"><p:outputLabel value="#{row[column.variable]}"/></f:facet>
<f:facet name="output"><p:inputText value="#{row[column.variable]}"/></f:facet>
</p:cellEditor>
</p:columns>
</p:dataTable>
Bean:
#ManagedBean (name = "bean")
#ViewScoped
public class Bean implements Serializable {
private ArrayList<TableRow> tblRows;
private ArrayList<Column> columns;
public ArrayList<TableRow> getTblRows(){
return tblRows;
}
public void setTblRows(ArrayList<TableRow> tblRows){
this.tblRows = tblRows;
}
public ArrayList<Column> getColumns(){
return columns;
}
public void setColumns(ArrayList<Column> columns){
this.columns = columns;
}
#PostConstruct
void init(){
columns = new ArrayList<>();
columns.add(new Column("Acronym", "col1");
columns.add(new Column("Meaning", "col2");
tblRows = new ArrayList<>();
tblRows.add(new TableRow("KFC", "Kentuky Fried Chicken"));
tblRows.add(new TableRow("TTYL", "Talk to you later"));
tblRows.add(new TableRow("BRB", "Be right back"));
}
}
TableRow Class:
public class TableRow {
private String col1;
private String col2;
public TableRow(String col1, String col2){
this.col1 = col1;
this.col2 = col2;
}
public String getCol1(){
return col1;
}
public void setCol1(String col1){
this.col1 = col1;
}
public String getCol2(){
return col2;
}
public void setCol2(String col2){
this.col2 = col2;
}
}
Column Class:
public class Column {
private String name;
private String variable;
public Column(String name, String variable){
this.name = name;
this.variable = variable;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public String getVariable(){
return variable;
}
public void setVariable(String variable){
this.variable = variable;
}
}
The reason why I did "col1" and "col2" instead of give distinct variable names is that the user can choose the column header names so I won't know what to name the variables ahead of time. I was wondering if anyone would like to enlighten me how I could handle this dynamically a little better. When the bean is initialized I don't know how many columns there are going to be or what the column names are going to be.

OrderList component works until submit the page

I have a page with an orderList component that is working partially.
This is the orderList
It has a list of names and also hold the ID for each system.
<p:orderList id="orderListAppList" value="#{manageApplications.listOfApplicationsAndIds}" var="app" controlsLocation="none" itemLabel="#{app.applicationName}" itemValue="#{app.applicationId}" >
<p:ajax event="select" listener="#{manageApplications.onSelect}"/>
<p:ajax event="unselect" listener="#{manageApplications.onUnselect}"/>
</p:orderList>
This is working as expected until I have added a button on the page that execute with ajax=false
When I click on this new button the error below is being thrown
javax.el.PropertyNotFoundException: Property 'applicationId' not found on type java.lang.String
This is the code that load the ArrayList used in the orderList component
#ManagedBean
#ViewScoped
public class ManageApplications implements Serializable {
private List<ApplicationNameAndId> listOfApplicationsAndIds = new ArrayList<ApplicationNameAndId>();
#PostConstruct
public void init() {
populateApplications();
}
private void populateApplications() {
// HERE I LOAD THE APPLICATION'S LIST FROM THE DATABASE
listOfApplicationsAndIds = manageDbApplications.getApplicationsListFromDatabase();
}
// GETTERS AND SETTERS
public List<ApplicationNameAndId> getListOfApplicationsAndIds() {
return listOfApplicationsAndIds;
}
public void setListOfApplicationsAndIds(List<ApplicationNameAndId> listOfApplicationsAndIds) {
this.listOfApplicationsAndIds = listOfApplicationsAndIds;
}
}
This is the type ApplicationNameAndId
public class ApplicationNameAndId implements Serializable {
private static final long serialVersionUID = -2031225157408650765L;
String applicationId;
String applicationName;
public ApplicationNameAndId() {
}
public ApplicationNameAndId(String appName, String appId) {
this.applicationId = appId;
this.applicationName = appName;
}
// GETTERS AND SETTERS
public String getApplicationId() {
return applicationId;
}
public void setApplicationId(String applicationId) {
this.applicationId = applicationId;
}
public String getApplicationName() {
return applicationName;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
}
My point is.
The component is working normally until I use some button with ajax=false.
<p:commandButton actionListener="#{manageApplications.selectQueuesToAddToAnExistingApplication(qmgr.queueManagerNameId)}"
oncomplete="PF('dlgAddQueuesToApplication').show();"
update=":j_idt3:addQueuesToApplicationPanel"
value="ADD QUEUE" ajax=false icon="fa fa-plus">
</p:commandButton>
Note that the "button" that I am talking about has nothing do to with the oederList component. Just adding a button that execute a listener (or something) with ajax=fase generate this error.
Any idea why?
The issue was related to the object that I was using.
The Application on my application's list is a Class that can be represented this way:
public class ApplicationNameAndId {
String applicationId;
String applicationName;
// CONSTRUCTOR
// GETTERS AND SETTERS HERE
}
The orderList component does not deal with your own object type, this expect to receive a List of Strings instead.
List<String> ListOfStringToBeUsed
For now I have adapted my code to return the list of Strings as expected.
When having more time I will create a custom converter class (to use my own object type) than post the how to here.
I hope this helps someone else,
Neliosam

JSF 2.2 ui:repeat setting input text component submitted value to null for immediate scenario during processDecodes

Env: WebSphere Liberty - 16_0_0_3 (JEE 7), JSF 2.2, PrimeFaces 6.0, Omnifaces 2.4, JDK 1.7+
I am using a primefaces remote command (by clicking on a link and calling the remote command by its name) to invoke an actionListener with immediate=true and partialSubmit=true. In simplistic world i am processing an output panel that internally has ui:repeat and ui:repeat has p:inputText.
<p:outputPanel id="_container">
<ui:repeat var="bean" value="#{controller.beanList}">
<p:inputText value="#{bean.firstName}" />
</ui:repeat>
</p:outputPanel>
<p:remoteCommand name="remoteCommand" actionListener="#{controller.process}" process="_container" update="_container" partialSubmit="true" immediate="true"/>
i am also executing facesContext.renderResponse() as the last statement in the listener.
Issues: Whatever value that gets submitted when ajax request is processed does not redisplays in the input text, it comes as blank. I can see the values submitted in the browser developer tools - on further investigation i found that UIRepeat is setting the submittedValue back to null during processDecodes (in the apply request values phase) through its method named UIRepeat.restoreDescendantComponentStates
But if i test the same scenario without ui:repeat then whatever value is submitted from the browser gets redisplayed i.e.
<p:outputPanel id="_container">
<p:inputText value="#{controller.firstName}" />
</p:outputPanel>
Not sure why ui:repeat is setting the submitted values as null.
Here is test code for backing beans as requested by #wtlucy
package test;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
#Named
#ViewScoped
public class Controller {
private String firstName;
private List<TestBean> beanList;
#PostConstruct
public void initialize(){
this.beanList = new ArrayList<>();
for(int i=0 ; i<2; i++){
this.beanList.add(new TestBean());
}
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public List<TestBean> getBeanList() {
return beanList;
}
}
package test;
public class TestBean {
private String firstName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}

New ViewScoped Bean instance on multiple fileUpload

I'm using primefaces 5.1.7 with wildfly and have a Problem when multiple pictures are uploaded at the same time.
I have multiple instances of a ViewScoped bean which are created (tested using breakpoints in #PostConstruct)
xhtml:
<h:form enctype="multipart/form-data" prependId="true">
<p:fileUpload fileUploadListener="#{myBean.handleFileUpload}"
mode="advanced" auto="true" update="list"
allowTypes="/(\.|\/)(gif|jpe?g|png)$/" multiple="true"/>
...
bean:
import javax.faces.view.ViewScoped;
#Named
#ViewScoped
public class MyBean implements Serializable {
...
public void handleFileUpload(FileUploadEvent event) {
...
}
}
This happens about 75% of the time but sometimes everything is just fine. Any idea how i can avoid those multiple instances?
a possible solution... (not tested!!):
Your Session scoped CDI Bean:
...
import javax.enterprise.context.SessionScoped
...
#SessionScoped
public class MySession implements Serializable {
public void doSomeWork(...) {
...
}
}
And your Conversation scoped CDI Bean:
...
import javax.inject.Named
import javax.enterprise.context.ConversationScoped
...
#Named
#ConversationScoped
public class MyBean implements Serializable {
#Inject
private Conversation conversation;
#Inject
private MySession mySession;
#PostConstruct
public void init() {
conversation.begin();
}
public void handleFileUpload(FileUploadEvent event) {
...
mySession.doSomeWork(...);
...
}
public void remove() {
conversation.end();
}
}
And your Controller:
#Named
public class MyController implements Serializable {
#Inject
private MyBean myBean;
public String doSomethingAfterAllUploadsAreFinished() {
...
myBean.remove();
return "SUCCESS";
}
}

primefaces, lazy loading datatable and jsf #conversation scope bean

I am using primefaces 3.2 and mojarra 2.1.7 with jboss 6.1. I am trying to build a lazy loading datatable. In one of the datatable columns I have a commandlink to call a edit method. If my bean is conversation scoped, the edit method is not called. If it is view scoped then it gets called. I cant have a conversation scoped bean to work with a lazy loading datatable?
#Named("userBean")
#Stateful
#ConversationScoped
#LoggedIn
public class UserBean implements Serializable, UserBeanLocal {
private static final long serialVersionUID = 1L;
#Inject
private Conversation conversation;
#EJB
private UserManagerBeanLocal userController;
#Inject
private transient FacesContext context;
private User user;
private LazyUserDataModel lazyModel;
public UserBean() {
user = new User();
}
#Override
#PostConstruct
public void createLazyDataModel() {
setLazyModel(new LazyUserDataModel(userController));
}
#Override
#PrePassivate
public void ejbPassivate(){
context = null;
}
#Override
#PostActivate
public void ejbActivate() {
}
#Override
public String create() {
this.conversation.begin();
return "create";
}
#Override
public String edit()
{
System.out.println("editing user");
return "create";
}
}
and
<h:form>
....
<p:dataTable id="userTable" var="usr" value="#{userBean.lazyModel}"
paginator="true"
paginatorTemplate="{FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="5,10,15" paginatorPosition="bottom" rows="10"
currentPageReportTemplate="{currentPage} de {totalPages}">
<p:column headerText="Username">
<h:outputText value="#{usr.name}" />
</p:column>
<p:column>
<p:commandLink value="Edit" action="#{userBean.edit}" actionListener="#{userBean.update}" update="userTable" />
</p:column>
</p:dataTable>
</h:form>
Can anybody help?
Thanks
Kelly
Well, it certainly should work. There is nothing obviously wrong with your code, but I suspect from the snippet you are providing that you have some "try and error" history...;-)
Try to debug the conversation and see which conversation is addressed (and when). Are you propagating the conversation-id correctly?
When you write ViewScoped works - did you include Seam 3 for bridging JSF-scopes to CDI, or can it be that you are mixing JSF and CDI scopes?
And: Don't put any EJB code in the constructor as you don't have any guarantee when (and how often) this will be called. Better to use #PostConstruct instead.
My problem was that I was not propagating the conversation id correctly.