View scoped JSF bean is used in multiple tabs - jsf

I have a simple example in which a JSF bean with view scope is called from different browser tabs
<!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:p="http://primefaces.org/ui">
<h:head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</h:head>
<h:body>
<h:form>
<p:panelGrid columns="3">
<p:autoComplete value="#{testBean.item}"
dropdown="true"
completeMethod="#{testBean.completeItems}"
var="item"
itemLabel="#{item}"
itemValue="#{item}" />
<p:commandButton value="submit" update="selectedValue" />
<p:outputLabel id="selectedValue" value="Selected item: #{testBean.item}" />
</p:panelGrid>
</h:form>
</h:body>
</html>
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import org.apache.commons.lang3.StringUtils;
import lombok.Getter;
import lombok.Setter;
#Getter
#Setter
#Named
#ViewScoped
public class TestBean implements Serializable {
private String item;
private final List<String> items = Arrays
.asList("item0", "item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9");
public List<String> completeItems(final String query) {
return items.stream()
.filter(i -> StringUtils.containsIgnoreCase(i, query))
.collect(Collectors.toList());
}
}
When I open the same page in a different browser tab, after selecting a value, I see the selected value from the other tab. I thought a view scoped bean would be created for each browser tab.
I am using Spring Boot v2.5.6, Glassfisch Jakarta Faces v2.3.17 and PrimeFaces v11.0.0
What I am doing wrong?

Related

PrimeFaces autoComplete dropdown not showing values

After updating from PrimeFaces 8 to 10 all the autoComplete components in my application do not show values when opening the dropdown menu
The list stays empty. When I debug into AutoComplete and the AutoCompleteRenderer, I can see that the suggestions for an empty query are generated
But later in the AutoCompleteRenderer the AutoComplete is a new instance (with same component id) without any suggestions.
This is my JSF page
<!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:p="http://primefaces.org/ui">
<h:head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</h:head>
<h:body>
<h:form>
<p:autoComplete value="#{testBean.item}"
dropdown="true"
completeMethod="#{testBean.completeItems}"
var="item"
itemLabel="#{item}"
itemValue="#{iten}" />
<p:outputLabel value="Selected item: #{testBean.item}" />
</h:form>
</h:body>
</html>
and my bean
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import org.apache.commons.lang3.StringUtils;
import lombok.Getter;
import lombok.Setter;
#Getter
#Setter
#ManagedBean
#ViewScoped
public class TestBean {
private String item;
private final List<String> items = Arrays
.asList("item-0", "item-1", "item-2", "item-3", "item-4", "item-5", "item-6", "item-7", "item-8", "item-9");
public List<String> completeItems(final String query) {
return items.stream()
.filter(i -> StringUtils.containsIgnoreCase(i, query))
.collect(Collectors.toList());
}
}
Any ideas what I am doing wrong?

p:growl does not show global faces message

I've the below command button and growl component:
<p:commandButton id="reservationAdd" actionListener=" {reservationBean.addReservation()}" value="Dodaj" oncomplete="PF('wdlgAddReservation').hide();" update=":frm" action="#{linkedTimelinesController.createTimeline()}">
<f:ajax execute="reservationAdd" onevent="click" listener="#{messageControler.eventAdded()}" render="dynamic"/>
</p:commandButton>
<p:growl id="msj" autoUpdate="true"/>
I'm adding a faces message as below:
#ManagedBean
public class MessageControler {
public void eventAdded(){
FacesContext.getCurrentInstance().addMessage(null,new FacesMessage(FacesMessage.SEVERITY_INFO,"Rezerwacja została dodana",null));
}
}
However, it does not show up in the growl component. How is this caused and how can I solve it?
My following minimal example works, perhaps you check the attributes of your commandButton
page.xhtml
<!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:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui">
<f:view>
<h:head/>
<h:body>
<h:form>
<p:commandButton value="Growl">
<f:ajax listener="#{page.triggerEvent}"/>
</p:commandButton>
<p:growl autoUpdate="true"/>
</h:form>
</h:body>
</f:view>
</html>
Page
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
#ManagedBean
public class Page {
public void triggerEvent() {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Test!", null));
}
}

PrimeFaces "tabChange" event with dynamic number of tabs

I need to get title of currently active tab inside my TabView. TabView is constructed with dynamic number of tabs with listener attached to "tabChanged":
<p:tabView value="#{bean.list}" var="listItem">
<p:ajax event="tabChange" listener="#{listenerBean.onChange}" />
<p:tab title="#{listItem.stringProperty}">
</p:tab>
</p:tabView>
The problem is that TabChangeEvent object received by onChange(TabChangeEvent event) always contains first tab instead of the active one.
public void onChange(TabChangeEvent event) {
event.getTab().getTitle(); //allways returns title of first tab
}
This behavior is only true for dynamic number of tabs in TabView if I define each tab explicitly, TabChangeEvent works fine.
Any suggestions? Thanks.
I use PrimeFaces 3.5 with JSF2.1 and Servlets 2.5
The following minimal example worked for my like a charm by printing the title of the tab to activate every time I click on it:
page.xhtml
<?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:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<f:view>
<h:head/>
<h:body>
<h:form>
<p:tabView value="#{bean.items}"
var="item">
<p:ajax event="tabChange"
listener="#{bean.printTitle}"
update="#form"/>
<p:tab title="#{item}">
</p:tab>
</p:tabView>
</h:form>
</h:body>
</f:view>
</html>
Bean.java
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import org.primefaces.event.TabChangeEvent;
#Named
#ViewScoped
public class Bean implements Serializable {
private final List<String> items = Arrays.asList("Hello", "This", "Is", "TabView");
public List<String> getItems() {
return items;
}
public void printTitle(TabChangeEvent event) {
System.out.println("title = [" + event.getTab().getTitle() + "]");
}
}

how to force refresh of my screen in JSF

in my JSF2 app I have ​​screens composed with :
Header
Body
In the header I have a combo list. At each change in value in the combo list I have an Ajax request that updates the data in the Body. So far everything is working properly. Now the home screen's structure should be change when the value of combo list change. To do this I have :
1 ManagedBean HomeBean that manage the home
1 ManagedBean HeaderBean that manage the header
2 object HomeScreen1.java and HomeScreen2.java that allows me to valued data from each screen
2 services HomeScreen1Loader.java and HomeScreen2Loader.java that manage loading of each type of screen
1 template home.xhtml
2 fichier home1.xhtml et home2.xhtml
When I log in to the application, I get the good page corresponding (Element type 1 => home page 1). But when I select a type 2 item, the actionListener methode is execute, ManagedBean's data was updated (for type 2 screen) , but the page does not updated. What do you do ?
HeaderBean.java :
package com.omb.view;
import java.io.Serializable;
import java.util.List;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.SelectItem;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import com.omb.exception.TechnicalException;
import com.omb.view.util.Constants;
import com.omb.view.util.FacesUtils;
#Controller
#Scope("session")
public class HeaderBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Log logger = LogFactory.getLog(HeaderBean.class);
private List<SelectItem> elementsDisplayed;
public void initComboList() throws FunctionnalException {
// init the combo list
}
public void elementChangeListener(ValueChangeEvent event) {
if (event.getNewValue() != null) {
// Do traitement....
ContextBean contextBean = (ContextBean) FacesUtils.getObjectInSession(ContextBean.CONTEXT_BEAN_NAME);
AbstractBean currentBean = (AbstractBean) FacesUtils.getObjectInSession(contextBean
.getCurrentBeanInSession());
try {
currentBean.refresh();
} catch (TechnicalException e) {
logger.error(e.getMessage(), e);
}
}
}
public String disconnect() {
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
return "/login.xhtml?faces-redirect=true";
}
public List<SelectItem> getElementsDisplayed() {
return elementsDisplayed;
}
public void setElementsDisplayed(List<SelectItem> elementsDisplayed) {
this.elementsDisplayed = elementsDisplayed;
}
}
ContextBean.java :
package com.omb.view;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import com.omb.view.util.Constants;
#Controller
#Scope("session")
public class ContextBean {
public final static String CONTEXT_BEAN_NAME = "contextBean";
private String templateHomeName;
private boolean defaultHome;
public String getTemplateHomeName() {
return this.templateHomeName;
}
public void setTemplateHomeName(String templateHomeName) {
this.templateHomeName = templateHomeName;
}
public boolean isDefaultHome() {
return this.defaultHome;
}
public void setDefaultHome(boolean defaultHome) {
this.defaultHome = defaultHome;
}
}
HomeBean.java :
package com.omb.view.home;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import com.omb.exception.FunctionnalException;
import com.omb.exception.TechnicalException;
import com.omb.view.AbstractBean;
import com.omb.view.util.Constants;
#Controller
#Scope("session")
public class HomeBean extends AbstractBean {
private static final Log logger = LogFactory.getLog(HomeBean.class);
public static final String HOME_1_NAME = "home1.xhtml";
public static final String HOME_2_NAME = "home2.xhtml";
#Autowired
private HomeScreen1 homeScreen1;
#Autowired
private HomeScreen2 homeScreen2;
#SuppressWarnings({"unchecked", "rawtypes"})
public String display() throws TechnicalException, FunctionnalException {
ContextBean context = (ContextBean) FacesUtils.getObjectInSession(ContextBean.CONTEXT_BEAN_NAME);
if (!isInitialized()) {
if (defaultHomeScreen == null) {
defaultHomeScreen = new DefaultHomeScreen();
}
if (eurHomeScreen == null) {
eurHomeScreen = new EurHomeScreen();
}
AbstractHomeScreenLoader loader = HomeScreenLoaderFactory.getLoader(getTypeElement());
if (Constants.CODE_TYPE_1.equals(getTypeElement()) {
loader.load(homeScreen1);
context.setTemplateHomeName(HOME_1_NAME);
} else {
loader.load(homeScreen2);
context.setTemplateHomeName(HOME_2_NAME);
}
setInitialized(true);
} else if (!upToDate) {
refresh();
}
return "home";
}
#SuppressWarnings({"unchecked", "rawtypes"})
public void refresh() throws TechnicalException {
upToDate = true;
AbstractHomeScreenLoader loader = HomeScreenLoaderFactory.getLoader(getTypeElement());
if (Constants.CODE_TYPE_1.equals(userContext.getCurrentHotelCountryId())) {
loader.refresh(homeScreen1);
} else {
loader.refresh(homeScreen2);
}
}
public HomeScreen1 getHomeScreen1() {
return this.homeScreen1;
}
public void setHomeScreen1(HomeScreen1 homeScreen1) {
this.homeScreen1 = homeScreen1;
}
public HomeScreen2 getHomeScreen2() {
return this.homeScreen2;
}
public void setHomeScreen2(HomeScreen2 homeScreen2) {
this.homeScreen2 = homeScreen2;
}
}
layout.xhtml main template of the application :
<?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:ui="http://java.sun.com/jsf/facelets"
xmlns:ice="http://www.icesoft.com/icefaces/component">
<h:head>
<title><ui:insert name="title">OMB</ui:insert></title>
<ice:outputStyle href="/xmlhttp/css/xp/xp.css" rel="stylesheet" type="text/css" />
<link type="text/css" rel="stylesheet"
href="#{facesContext.externalContext.requestContextPath}/resources/css/style.css" />
</h:head>
<h:body>
<h:panelGroup id="page" styleClass="mainMaster" layout="block">
<h:panelGroup id="header" styleClass="header" layout="block">
<ui:insert name="header">
<ui:include
src="/pages/layer/header/#{contextBean.templateHeaderName}" />
</ui:insert>
</h:panelGroup>
<h:panelGroup id="headerMenu" styleClass="menu" layout="block">
<ui:insert name="buttons">
<ui:include
src="/pages/layer/menu/#{contextBean.templateMenuButtonName}" />
</ui:insert>
</h:panelGroup>
<h:panelGroup id="main" styleClass="mainContent" layout="block">
<h:panelGroup id="content" styleClass="content" layout="block">
<ui:insert name="content" />
</h:panelGroup>
</h:panelGroup>
<h:panelGroup id="footer" styleClass="footer" layout="block">
<ui:insert name="footer">
<ui:include src="/pages/layer/footer/footer.xhtml" />
</ui:insert>
</h:panelGroup>
</h:panelGroup>
</h:body>
</html>
header.xhtml, page which manage the combo list:
<?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:ui="http://java.sun.com/jsf/facelets">
<body>
<ui:composition>
<ice:form id="headerForm" xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ice="http://www.icesoft.com/icefaces/component"
xmlns:ace="http://www.icefaces.org/icefaces/components"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:panelGroup styleClass="logo" layout="block">
<ice:graphicImage styleClass="imgLogoHR"
value="#{facesContext.externalContext.requestContextPath}/resources/images/common/logo/Logo.png" />
<h:panelGroup styleClass="loginArea" layout="block">
<h:panelGroup styleClass="area" layout="block">
<h:panelGroup styleClass="comboHotel" layout="block">
<ace:simpleSelectOneMenu id="selectCurrentElement"
value="#{headerBean.currentElementDisplayed}"
valueChangeListener="#{headerBean.elementChangeListener}"
labelPosition="left" indicatorPosition="left" required="false"
rendered="#{not empty headerBean.elementsDisplayed}">
<f:selectItems value="#{headerBean.elementsDisplayed}" />
<ace:ajax execute="#this" render="#all" />
</ace:simpleSelectOneMenu>
</h:panelGroup>
</h:panelGroup>
</h:panelGroup>
</h:panelGroup>
</ice:form>
</ui:composition>
</body>
</html>
home.xhtml main template of home page and component should be refresh:
<?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:ui="http://java.sun.com/jsf/facelets"
xmlns:ice="http://www.icesoft.com/icefaces/component"
xmlns:ace="http://www.icefaces.org/icefaces/components">
<h:body>
<ui:composition template="/pages/layer/layout.xhtml">
<ui:define name="content">
<ui:include
src="/pages/home/#{contextBean.templateHomeName}" />
</ui:define>
</ui:composition>
</h:body>
</html>
I found the solution, the problem came to the ManagedBean ContextBean's templateHomeName attribute that was not properly valued. I added in loader.load(homeScreen1) and refresh and everything it's ok. I should upgrade my JSF version : 2.1.0-b11 to 2.1.26 because I had an error when refresh.

POST http://localhost:8080/Languages/WEB-INF/inventory.xhtml 404 (No Encontrado)

I am using facelets in my views with JSF 2.0. I have two backing beans, two xhtml view files and another xhtml template file. When I have the second view in the webapp directory and I change the language all is Ok but when I put mi second view into the WEB-INF folder and I change the language I have the following error in the chrome network console:
POST http://localhost:8080/Languages/WEB-INF/inventory.xhtml 404 (No Encontrado)
These are my backings beans:
LanguageBean.java:
package com.testapp;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
#ManagedBean(name = "language")
#SessionScoped
public class LanguageBean implements Serializable {
private static final long serialVersionUID = 1L;
private String localeCode;
private static final String DEFAULT_LOCAL_CODE = "es";
private static Map<String, Object> countries;
static {
countries = new LinkedHashMap<String, Object>();
countries.put("English", Locale.ENGLISH); // label, value
countries.put("Français", Locale.FRENCH);
countries.put("Español", new Locale("es"));
}
public Map<String, Object> getCountriesInMap() {
return countries;
}
public String getLocaleCode() {
if(localeCode == null) {
localeCode = DEFAULT_LOCAL_CODE;
}
return localeCode;
}
public void setLocaleCode(String localeCode) {
this.localeCode = localeCode;
}
public void changeLanguage() {
// loop a map to compare the locale code
for (Map.Entry<String, Object> entry : countries.entrySet()) {
if (entry.getValue().toString().equals(localeCode)) {
FacesContext.getCurrentInstance().getViewRoot().setLocale((Locale) entry.getValue());
}
}
}
}
InventoryBean.java:
package com.testapp;
import java.io.Serializable;
import java.util.Locale;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#ManagedBean(name = "inventory")
#ViewScoped
public class InventoryBean implements Serializable {
private static final long serialVersionUID = 4576404491584185639L;
Logger logger = LoggerFactory.getLogger(InventoryBean.class);
// Failing path
private static final String INVENTORY_MAIN_VIEW = "/WEB-INF/inventory";
// Working path
// private static final String INVENTORY_MAIN_VIEW = "/views/inventory";
public String findProducts() {
try {
System.err.println("In inventory backing bean");
} catch (Exception exception) {
}
return INVENTORY_MAIN_VIEW;
}
public void changeLanguage() {
FacesContext.getCurrentInstance().getViewRoot().setLocale((Locale.ENGLISH));
}
}
These are the view files:
default.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets"
template="/WEB-INF/header.xhtml">
<ui:define name="contentBody">
<f:view locale="#{language.localeCode}">
<h:form id="contentForm">
<h:outputText value="#{msg['internationalization.test']}" />
<p:commandButton id="gettingProducts"
value="Getting the products..." action="#{inventory.findProducts}" />
</h:form>
</f:view>
</ui:define>
</ui:composition>
inventory.xhtml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets"
template="/WEB-INF/header.xhtml">
<ui:define name="contentBody">
<f:view locale="#{language.localeCode}">
<h:form id="contentForm">
<h:outputText value="#{msg['internationalization.test']}" />
</h:form>
</f:view>
</ui:define>
</ui:composition>
And this is the template xhtml file (header.xhtml):
<?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:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets">
<f:view contentType="text/html">
<h:head>
</h:head>
<h:body>
<div id="content">
<h:form id="headerForm">
<h:panelGrid columns="2">
<p:outputLabel value="Language :" for="languages" />
<h:selectOneMenu required="true" id="languages"
value="#{language.localeCode}">
<f:selectItems value="#{language.countriesInMap}" />
<p:ajax event="change" update="#all"
listener="#{language.changeLanguage}" />
</h:selectOneMenu>
</h:panelGrid>
</h:form>
<ui:insert name="contentBody" />
</div>
</h:body>
</f:view>
</html>
What is happening?
A HTTP 404 error simply means "Page Not Found". And indeed, files inside /WEB-INF folder are not publicly accessible. Put files which are intented to be publicly accessible outside /WEB-INF folder.
Note that this has further completely nothing to do with internationalization. You'd have had exactly the same problem when not doing anything with regard to internationalization (e.g. a simple navigation).
See also:
Which XHTML files do I need to put in /WEB-INF and which not?

Resources