PrimeFaces "tabChange" event with dynamic number of tabs - jsf

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

Related

View scoped JSF bean is used in multiple tabs

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?

p:selectManyMenu(with checkbox): Add item and select checkbox at the same time

xhtml code
<p:selectManyMenu id="menuid"
value="#{bean.selectedActivities}"
showCheckbox="true" scrollable="true" scrollHeight="150">
<f:selectItems value="#{bean.activities}" var="activity" itemValue="#{activity}"
itemLabel="#{activity}" />
</p:selectManyMenu>
<p:commandButton value="ADD ACTIVITY" id="addId">
<p:ajax event="click" process="#this" update="menuid" listener="#{bean.addActivity()}"/>
</p:commandButton>
Bean:
private List<String> selectedActivities = new ArrayList<>();
private List<String> activities = new ArrayList<>();
int index = 1;
public void addActivity(){
String activity = "Activity "+ (index ++);
activities.add(activity);
selectedActivities.add(activity);
}
This code is adding new item to the manyMenu but the checkbox is not selected.
Apart from some missing annotations I don't see much wrong. In any case, here is a tested solution based on your code that should work. First let's define the view. This is basically the same as in your example:
<?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:f="http://xmlns.jcp.org/jsf/core" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>SelectMany Example</title>
</h:head>
<h:body>
<h:form>
<p:selectManyMenu id="menuid"
value="#{selectManyBackingBean.selectedActivities}"
showCheckbox="true" scrollable="true" scrollHeight="150">
<f:selectItems value="#{selectManyBackingBean.activities}" var="activity" itemValue="#{activity}"
itemLabel="#{activity}" />
</p:selectManyMenu>
<p:commandButton value="ADD ACTIVITY" id="addId">
<p:ajax event="click" process="#form" update="menuid"
listener="#{selectManyBackingBean.onAddActivity}"/>
</p:commandButton>
</h:form>
</h:body>
</html>
Then, we define the backing bean:
#Data
#Named
#ViewScoped
public class SelectManyBackingBean implements Serializable {
private List<String> selectedActivities;
private List<String> activities;
private int index;
#PostConstruct
private void init() {
activities = new ArrayList<>();
selectedActivities = new ArrayList<>();
index = 0;
}
public void onAddActivity(){
String activity = "Activity " + (index++);
activities.add(activity);
selectedActivities.add(activity);
}
}
This should give you the expected behavior. Clicking three times on the ADD ACTIVITY button now yields the following result:
Notice the subtle change in the command button from process="#this" to process="#form". This will make sure that any changes you make in the component are also included in the form submission. If you keep it at the original value, any clicks on the check boxes in the menu will not be kept and will reset entries to the previous value when you press the command button (this is because the component in question is not included when the life cycle executes).

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

Bean method not being invoked

Turns out that faces-config.xml was overriding the inline annotation ViewScoped in my Controller class w/ RequestScoped. Fixed it and that seem to solve the problem.
This question does not have an answer here commandButton/commandLink/ajax action/listener method not invoked or input value not updated and if you think it does, please provide a working fix/example using primefaces fluidGrid extension.
I am using primefaces ui exension fluidGrid : http://fractalsoft.net/primeext-showcase-mojarra/sections/fluidgrid/dynamic.jsf
I can't seem to invoke profileController.testControl() , if I place the commandButton outside of the fluidGrid it works fine , but not within the grid. Any ideas?
I've tested by changing my bean to #ViewScoped , there are no nested forms etc.
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
xmlns:p="http://primefaces.org/ui"
xmlns:pe="http://primefaces.org/ui/extensions"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
<composite:attribute name="resultList" />
</composite:interface>
<composite:implementation>
<h:form id="form1" rendered="true">
<!-- Grid -->
<pe:fluidGrid value="#{resultList}" var="showvar" hGutter="20" rowKeyVar="rowKey" fitWidth="true" hasImages="true" rendered="true" >
<pe:fluidGridItem rendered="true" >
<p:panel id="seriesPanel" rendered="#{showvar.isSeries()}"></p:panel>
<p:panel id="episodePanel" rendered="#{!showvar.isSeries()}" >
<p:commandButton value="click me" action="#{profileController.testControl()}"/>
<!-- another button attempt that doesn't work -->
<p:commandButton process="fluidGrid" value="click me again" ajax="false" actionListener="#{profileController.testControlEvent()}" />
</p:panel>
</pe:fluidGridItem>
</pe:fluidGrid>
</h:form>
</composite:implementation>
</html>
//Tried with #ViewScoped as well
#Model
public class ProfileController {
public void testControl(){
log.info("---------------------------------------");
log.info("TEST CONTROL CLICKED");
log.info("---------------------------------------");
}
public void testControlEvent(ActionEvent actionEvent){
log.info("---------------------------------------");
log.info("TEST CONTROL CLICKED");
log.info("---------------------------------------");
}
}
I've tried the simple example of having command button inside fluidGrid and it works here.
XHTML File
<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:pe="http://primefaces.org/ui/extensions"
xmlns:p="http://primefaces.org/ui">
<h:head>
</h:head>
<h:body>
<h:form id="login">
<pe:fluidGrid value="#{tBean.images}" var="showvar" hGutter="20"
rowKeyVar="rowKey" fitWidth="true" hasImages="true" rendered="true">
<pe:fluidGridItem rendered="true">
<p:commandButton value="click me" action="#{tBean.doAction}" />
</pe:fluidGridItem>
</pe:fluidGrid>
</h:form>
</h:body>
</html>
ManagedBean code
package bean;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import org.primefaces.extensions.model.fluidgrid.FluidGridItem;
#ManagedBean(name = "tBean")
#ViewScoped
public class TestBean implements Serializable{
private List<FluidGridItem> images;
#PostConstruct
protected void initialize() {
images = new ArrayList<FluidGridItem>();
for (int j = 0; j < 3; j++) {
for (int i = 1; i <= 10; i++) {
images.add(new FluidGridItem("i" + 1));
}
}
}
public void doAction() {
System.out.println("Im doing action");
}
public List<FluidGridItem> getImages() {
return images;
}
}
Try above and see if that works for you. If it works then try to use in your implementation.
I have sometimes problem with rendered and button not being invoked. Make sure that your rendered="#{!showvar.isSeries()}"
works correct or remove it and try again.
u must update your form i think in your page add
in the button update=":form1:fluidGrid" if dosent work make the ajax on true it helped to solve a problem like this i hope it will help you

Datatable items set to null upon commandLink action

If I run the app and click the first p:commandLink, the item gets set as expected. When clicking another p:commandLink the previous one will be removed (null). The h:commandLink does not have that problem.
If I add :theform:output to the update parameter of the p:commandLink, the previously clicked item stops disappearing.
What is the reason for this behaviour?
Env
Java 1.7.45
Netbeans 7.4
JSF Mojarra 2.2.4
Primefaces 4.0
Index.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:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Test</title>
</h:head>
<h:body>
<h:form id="theform">
<h:panelGroup layout="block" styleClass="tableBorder" id="tableWrapper">
<h:dataTable value="#{xtestListBean.list}" var="item">
<h:column>
<h:outputText value="#" styleClass="tableItem" />
</h:column>
<h:column>
<h:commandLink
value="#{item.name}"
actionListener="#{xtestEntityBean.setItem(item)}">
<f:ajax render=":theform:tableWrapper" />
</h:commandLink>
<p:commandLink
value="#{item.name}"
actionListener="#{xtestEntityBean.setItem(item)}"
update=":theform:tableWrapper">
</p:commandLink>
</h:column>
</h:dataTable>
</h:panelGroup>
<h:inputText value="#{xtestEntityBean.item.name}" size="40" id="output" />
</h:form>
</h:body>
</html>
XtestListBean.java
package beans;
import dto.Item;
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 XtestListBean implements Serializable {
private List<Item> list;
public XtestListBean() {
list = new ArrayList();
list.add(new Item("Test 1"));
list.add(new Item("Test 2"));
list.add(new Item("Test 3"));
list.add(new Item("Test 4"));
list.add(new Item("Test 5"));
list.add(new Item("Test 6"));
list.add(new Item("Test 7"));
}
public List<Item> getList() {
return list;
}
}
XtestEntityBean.java
package beans;
import dto.Item;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
#ManagedBean
#ViewScoped
public class XtestEntityBean implements Serializable {
private Item item = new Item();
public XtestEntityBean() {
}
public void setItem(Item item) {
this.item = item;
}
public Item getItem() {
return item;
}
public String getVersion() {
return FacesContext.class.getPackage().getImplementationTitle()
+ " version "
+ FacesContext.class.getPackage().getImplementationVersion();
}
}
That only looks like so. It's just that the <h:inputText value="#{xtestEntityBean.item.name}"> is also processed when the <p:commandLink> is invoked. In effects, the item's name is submitted with an empty/null value and that get reflected in the table. It's not the whole Item instance which became null or so.
How is this caused? Well, the <f:ajax execute> defaults to #this, meaning that only the current component (i.e. the command link) is processed during the form submit. However, the PrimeFaces equivalent, <p:commandLink process> defaults to #form, meaning that the entire parent form is processed during form submit, including that empty input field referring the item name.
If you explicitly set <p:commandLink process> to #this, the same as <f:ajax execute>'s default, then it should work as intented.
<p:commandLink
value="#{item.name}"
actionListener="#{xtestEntityBean.setItem(item)}"
process="#this" update=":theform:tableWrapper">
</p:commandLink>

Resources