add dynamically tab to rich:tabPanel - jsf-2

i' m developing my first jsf2 richfaces application , and now i am faced with the following problem :
i have the following menù
<rich:panelMenu >
<rich:panelMenuGroup >
<rich:panelMenuItem label="Users" name="Users " />
<rich:panelMenuItem label="Orders" name="Orders" />
</rich:panelMenuGroup>
</rich:panelMenu>
I want that when click on the panelMenuItem is created a new tab and within of this new tab must insert a table that contains all users of the my application
i saw a few example of this type
<rich:tabPanel switchType="client" id="tabPanel">
<c:forEach items="#{handlerTab.tabsName}" var="tabName">
<rich:tab name = ... >
</rich:tab>
</c:foreach>
but don't know as insert a table in my new tab
how can i do?
tanks for your reply, but i don't want declare all tabs in the view , i want add and remove tab dynamically , now i have managed to do this
<rich:panelMenu >
<rich:panelMenuGroup >
<rich:panelMenuItem label="Users" name="Users" action="#{tabsBean.createTabs()}" render="tabs" />
<rich:panelMenuItem label="Orders" name="Orders" action="#{tabsBean.createTabs()}" render="tabs" />
</rich:panelMenuGroup>
<h:panelGrid id="tabs" binding="#{tabsBean.panelGrid}"/>
then i have a bean that manage the tabs
#ManagedBean
#SessionScoped
public class TabsBean {
private HtmlPanelGrid panelGrid;
private Integer numOfTabs;
#PostConstruct
public void init(){numOfTabs=1;}
public Integer getNumOfTabs() {
return numOfTabs;
}
public void setNumOfTabs(Integer numOfTabs) {
this.numOfTabs = numOfTabs;
}
public TabsBean() {
}
public HtmlPanelGrid getPanelGrid() {
return panelGrid;
}
public void setPanelGrid(HtmlPanelGrid panelGrid) {
this.panelGrid = panelGrid;
}
public void createTabs (){
FacesContext context = FacesContext.getCurrentInstance();
Application application = context.getApplication();
UITabPanel tabPanel = (UITabPanel)application.createComponent(UITabPanel.COMPONENT_TYPE);
tabPanel.setSwitchType(SwitchType.ajax);
for (int i=0; i<numOfTabs; i++){
UITab tab = new UITab();
tab = (UITab)application.createComponent(UITab.COMPONENT_TYPE);
tab.setName("User Count "+i);
tabPanel.getChildren().add(tab);
}
numOfTabs++;
panelGrid.getChildren().clear();
panelGrid.getChildren().add(tabPanel);
}
}
now my problem is that i must add of components a this tabs (datatable that contains all user , form for insert a user and other)
how can i do?

If you want the menu to open tabs with specific content it's better if you pre-create the tab and hide it, showing it (rendering) on request. To create the Users do the following:
Create a User class (if you don't have one)
public class User {
private String name;
private String country;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
Create a UserTab managed bean
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 UserTab implements Serializable {
private List<User> users;
private boolean rendered;
public UserTab() {
//Initialize list
users = new ArrayList();
//Initialize rendered attribute
rendered = false;
//Replace this for your User data retrieving method
createDummyUsers();
}
private void createDummyUsers() {
User user = new User();
user.setName("John Doe");
user.setCountry("USA");
users.add(user);
user = new User();
user.setName("Bill Doe");
user.setCountry("Canada");
users.add(user);
user = new User();
user.setName("Winston Doe");
user.setCountry("England");
users.add(user);
}
public void showTab() {
rendered = true;
}
public void hideTab() {
rendered = false;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
public boolean isRendered() {
return rendered;
}
public void setRendered(boolean rendered) {
this.rendered = rendered;
}
}
Create a tab handler:
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class TabHandler implements Serializable {
private String activeTab;
public TabHandler() {
activeTab = "none";
}
public String getActiveTab() {
return activeTab;
}
public void setActiveTab(String activeTab) {
this.activeTab = activeTab;
}
}
Create the view
<h:body>
<h:form>
<rich:panelMenu >
<rich:panelMenuGroup >
<rich:panelMenuItem label="Users" name="Users"
actionListener="#{userTab.showTab()}">
<f:ajax event="select"
execute="#this"
render="tabPanel"
listener="#{tabHandler.setActiveTab('usersTab')}"/>
</rich:panelMenuItem>
<rich:panelMenuItem label="Orders" name="Orders" />
</rich:panelMenuGroup>
</rich:panelMenu>
<rich:tabPanel id="tabPanel"
switchType="client"
activeItem="#{tabHandler.activeTab}">
<rich:tab id="mainTab">
</rich:tab>
<rich:tab id="usersTab"
rendered="#{userTab.rendered}">
<f:facet name="header">
<h:outputLabel value="Users"/>
<h:commandLink value=" X" actionListener="#{userTab.hideTab()}"/>
</f:facet>
<rich:dataTable value="#{userTab.users}"
var="user">
<rich:column>
#{user.name}
</rich:column>
<rich:column>
#{user.country}
</rich:column>
</rich:dataTable>
</rich:tab>
</rich:tabPanel>
</h:form>
</h:body>
Do the same steps and add the required tags for the Order class.
See:
RichFaces dynamic TabPanel

c:forEach is not an option if you want to add the tabs by click.
What you want is to add components dynamically:
How to dynamically add JSF components
Dynamically added input field in ui:repeat is not processed during form submit
How to Dynamically adding fields in JSF?

Related

How to avoid sharing of values stored in StateHelper of composite components placed inside PrimeFaces datatable?

My composite components share values stored in StateHelper when placed inside PrimeFaces DataTable. The most of examples about keeping component state that I've seen suggest to use getStateHelper().put()/eval() methods of UINamingContainer. I do use these methods but without luck. How to do that properly? (currently I use workaround described in the end of this post)
To illustrate the issue I've created click counter based on PrimeFaces commandLink component. In the example below two counters that are outside of dataTable work as expected. But all counters that appear inside dataTable share the same counter value (clicking on any one of them continues common value).
Update:
I've figured out that to allow sorting (for example) to work correctly inside datatable I need to bind my component to certain raw somehow. And "shared" state helper allows to do exactly that. So now I specify row key as an attribute and have updated methods to store state. There is no question if this way is correct.
Update for counterLink.xhtml:
<composite:interface componentType="CounterLink2Component">
<composite:attribute name="key" type="java.io.Serializable"/>
</composite:interface>
And CounterLinkComponent.java now is:
#FacesComponent("CounterLinkComponent")
public class CounterLinkComponent extends UINamingContainer {
private enum PropertyKeys {
COUNTER_VALUE
}
public void count() {
storeInstanceValue(PropertyKeys.COUNTER_VALUE.toString(), getCounterValue() + 1);
}
public Integer getCounterValue(){
return (Integer) evalInstanceValue(PropertyKeys.COUNTER_VALUE.toString(), 0);
}
private Serializable getKeyAttr() {
return (Serializable) getAttributes().get("key");
}
private void storeInstanceValue(String key, Object value) {
Serializable subkey = getKeyAttr();
if (subkey == null) {
getStateHelper().put(key, value);
} else {
getStateHelper().put(subkey, key, value);
}
}
private Object getInstanceValue(String key) {
Serializable subkey = getKeyAttr();
if (subkey == null) {
return getStateHelper().eval(key);
} else {
return ((Map) getStateHelper().eval(subkey, Collections.emptyMap())).get(key);
}
}
private Object evalInstanceValue(String key, Object _default) {
Object result = getInstanceValue(key);
return result != null ? result : _default;
}
}
Original example:
Primefaces 5.0, Glassfish 4.
counterLink.xhtml:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:composite="http://java.sun.com/jsf/composite"
xmlns:p="http://primefaces.org/ui">
<composite:interface componentType="CounterLinkComponent">
</composite:interface>
<composite:implementation>
<p:commandLink action="#{cc.count()}" partialSubmit="true" update="#this">
<h:outputText value="#{cc.counterValue}"/>
</p:commandLink>
</composite:implementation>
</html>
CounterLinkComponent.java:
import javax.faces.component.FacesComponent;
import javax.faces.component.UINamingContainer;
import java.io.Serializable;
#FacesComponent("CounterLinkComponent")
public class CounterLinkComponent extends UINamingContainer {
private enum PropertyKeys {
COUNTER_VALUE
}
public void count() {
getStateHelper().put(PropertyKeys.COUNTER_VALUE, getCounterValue() + 1);
}
public Integer getCounterValue(){
return (Integer) getStateHelper().eval(PropertyKeys.COUNTER_VALUE, 0);
}
}
Usage example:
<h:form>
<p:panelGrid columns="1">
<cmp:counterLink/>
<cmp:counterLink/>
<p:dataTable var="item" value="#{counterLinkStoreBean.itemList}">
<p:column headerText="Name">
#{item.name}
</p:column>
<p:column headerText="Counter">
<cmp:counterLink/>
</p:column>
</p:dataTable>
</p:panelGrid>
</h:form>
Backing bean for this example (just creates several items):
#Named
#ViewScoped
public class CounterLinkStoreBean implements Serializable {
private List<Item> itemList;
#PostConstruct
private void init() {
itemList = new ArrayList<Item>();
itemList.add(new Item("Test 1"));
itemList.add(new Item("Test 2"));
itemList.add(new Item("Test 3"));
}
public List<Item> getItemList() {
return itemList;
}
public static class Item {
private final String name;
public Item(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
}
In my case I can use workaround storing values in a map with component clientId as a secondary key:
private void storeInstanceValue(Serializable key, Object value) {
getStateHelper().put(key, getClientId(), value);
}
private Object getInstanceValue(Serializable key) {
return ((Map)getStateHelper().eval(key, Collections.emptyMap())).get(getClientId());
}
Is there more natural solution?

PrimeFaces DataTable with required Radiobutton

I am using PrimeFaces DataTable with Radiobuttons inside a one wizard tab. Is possible to somehow set Radiobuttons like required?
User shouldn't go to the next wizard tab until he will choose one option in DataTable with Radiobuttons.
Or have you any ideas how to resolve this problem? Thanks for any replies!
JSP page
<p:tab id="test" title="Test">
<p:panel header="Term page">
<p:dataTable id="collection" value="#{register.dataList}" var="dl" rowKey="#{dl.c_id}" selection="#{register.selectedTerm}"">
<p:column selectionMode="single" style="width:2%" />
<p:column>
#{dl.c_id}
</p:column>
</p:dataTable>
</p:panel>
</p:tab>
You can check for data selection in flowListener of <p:wizard> tag that is triggered when next/previous buttons are clicked and conditionally add FacesMessage:
public String onFlowProcess(FlowEvent event) {
String current = event.getOldStep();
String next = event.getNewStep();
boolean proceed = true;
if(current.equals("first") && next.equals("second") && (selectedData == null)) {
//proceed only when data was selected and user is moving to the next step
FacesMessage facesMessage = new FacesMessage("You need to make a selection in a datatable to proceed!");
FacesContext.getCurrentInstance().addMessage("form:selection", facesMessage);
proceed = false;
}
return proceed ? next : current;
}
The full example is provided below.
The view:
<h:form id="form">
<p:wizard widgetVar="wiz" flowListener="#{q16439053Bean.onFlowProcess}">
<p:tab id="first" title="First">
<p:message for="selection"/>
<p:panel id="selection" header="Term page">
<p:dataTable id="collection" value="#{q16439053Bean.list}" var="data" rowKey="#{data.name}" selection="#{q16439053Bean.selectedData}">
<p:column selectionMode="single" style="width:2%" />
<p:column>
#{data.name}
</p:column>
</p:dataTable>
</p:panel>
</p:tab>
<p:tab id="second" title="Second">
Done!
</p:tab>
</p:wizard>
</h:form>
The bean:
#ManagedBean
#ViewScoped
public class Q16439053Bean implements Serializable {
private List<Data> list;
private Data selectedData;
public List<Data> getList() {
return list;
}
public void setList(List<Data> list) {
this.list = list;
}
public Data getSelectedData() {
return selectedData;
}
public void setSelectedData(Data selectedData) {
this.selectedData = selectedData;
}
public class Data {
private String name;
private String value;
public Data() {
}
public Data(String name, String value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
public Q16439053Bean() {
list = new ArrayList<Data>();
Data d;
d = new Data("name", "value");
list.add(d);
d = new Data("name1", "value1");
list.add(d);
d = new Data("name2", "value2");
list.add(d);
d = new Data("name3", "value3");
list.add(d);
}
public String onFlowProcess(FlowEvent event) {
String current = event.getOldStep();
String next = event.getNewStep();
boolean proceed = true;
if(current.equals("first") && next.equals("second") && (selectedData == null)) {
FacesMessage facesMessage = new FacesMessage("You need to make a selection in a datatable to proceed!");
FacesContext.getCurrentInstance().addMessage("form:selection", facesMessage);
proceed = false;
}
return proceed ? next : current;
}
}

How to do a JSF nested table binding. Target Unreachable exception

I'm trying to implement on JSF data table a binding to a backed bean value with a nested data table with a binding to a value of the current data row, see this code:
<h:form prependId="false">
<h:dataTable binding="#{tableBean.mainDataTable}" var="row" >
<h:column>
<h:dataTable binding="#{row.nestedDataTable}" />
</h:column>
</h:dataTable>
</h:form>
And these are the backed beans and data:
#ManagedBean
#SessionScoped
public class TableBean implements Serializable{
private HtmlDataTable mainDataTable;
private List<TableBeanData> tableBeanDataLst;
public TableBean() {
tableBeanDataLst = new ArrayList<TableBeanData>();
DataModel<TableBeanData> mainDataModel =new ListDataModel<TableBeanData>(tableBeanDataLst);
mainDataTable = new HtmlDataTable();
mainDataTable.setValue(mainDataModel);
}
public HtmlDataTable getMainDataTable() {
return mainDataTable;
}
public void setMainDataTable(HtmlDataTable mainDataTable) {
this.mainDataTable = mainDataTable;
}
public List<TableBeanData> getTableBeanDataLst() {
return tableBeanDataLst;
}
public void setTableBeanDataLst(List<TableBeanData> tableBeanDataLst) {
this.tableBeanDataLst = tableBeanDataLst;
}
}
public class TableBeanData implements Serializable{
private HtmlDataTable nestedDataTable;
public TableBeanData (){
nestedDataTable = new HtmlDataTable();
}
public HtmlDataTable getNestedDataTable() {
return nestedDataTable;
}
public void setNestedDataTable(HtmlDataTable nestedDataTable) {
this.nestedDataTable = nestedDataTable;
}
}
But I get a property not found exception:
/index.xhtml #17,69 binding="#{row.nestedDataTable}": Target Unreachable, indentified 'row' resolved to null
I don't understand well, because I'm Initializing the datatables in the constructors.
How can I resolve this type of error to allow this configuration of table, nested table and data bindings?

PrimeFaces picklist target not populated

I'm implementing a PrimeFaces picklist in a dialog. Everytime the dialog is shown the contents of the target of the picklist should change depending on a list entry shown before. Before I open the dialog I fill the target of the picklist in ProdukteBean.onEditProdukt(..) with the appropriate values. Unfortunately these target values do not show up in the target container. Here are the relevant code pieces:
list.xhtml:
<p:commandLink id="editButton" update=":dialogForm:editDialogPanel" title="Edit"
oncomplete="produktDialog.show();"
process="#this" partialSubmit="true"
action="#{produkteBean.onEditProdukt}">
<h:outputText value="Bearbeiten" />
<f:setPropertyActionListener value="#{p}" target="#{produkteBean.produkt}" />
</p:commandLink>
dialog.xhtml:
<!-- ... -->
<p:dialog id="dialog" header="Produkt-Details" widgetVar="produktDialog" appendToBody="true" showEffect="explode" hideEffect="explode" modal="true" width="500">
<p:messages id="msgs"/>
<h:form id="dialogForm">
<!-- ... -->
<p:pickList id="produkteDatenList" var="proddat" value="#{produkteBean.datenList}"
itemLabel="#{proddat.bezeichnung}" itemValue="#{proddat}"
converter="produktDatConverter"/>
<!-- ... -->
</h:form>
</p:dialog>
ProdukteBean.java:
#Named("produkteBean")
#ViewScoped // #SessionScoped // #ViewScoped
public class ProdukteBean implements Serializable {
#Inject #Transient private ProdukteService produkteService;
#Inject #Transient private DatenService datenService;
#Inject()
private ProdukteDatenBean produkteDatenBean;
private DualListModel<Dat> datenList = new DualListModel<Dat>();
private Dat dat = null;
public ProdukteBean() {
}
#PostConstruct
private void init() {
getAll();
}
private void getAll() {
logger.debug("getAll()");
getAllProdukte();
getAllDaten();
}
private void getAllDaten() {
logger.debug("getAllDaten()");
List<Dat> source = new ArrayList<Dat>();
source.addAll(datenService.list());
List<Dat> target = new ArrayList<Dat>();
if (produkt.getDaten() != null) {
logger.debug("adding " + produkt.getDaten().size() + " daten to produkt " + produkt.getName());
target.addAll(produkt.getDaten());
}
DualListModel<Dat> datenList = new DualListModel<Dat>();
datenList.setSource(source);
datenList.setTarget(target);
setDatenList(datenList);
}
public List<Produkt> getAllProdukte() {
logger.debug("getAllProdukte()");
return produkteService.list();
}
public void onEditProdukt() {
onEditProdukt(null);
}
public void onEditProdukt(ActionEvent actionEvent) {
logger.debug("onEditProdukt: " + ReflectionToStringBuilder.toString(produkt));
if (produkt != null) {
setSelectedEinheit(produkt.getEinheit());
getAllDaten();
}
FacesMessage msg = new FacesMessage("Produkt ausgewählt", produkt.getName());
FacesContext.getCurrentInstance().addMessage(null, msg);
}
/**
* #return the einheitList
*/
public List<Einheit> getEinheitList() {
if (einheitList == null) {
einheitList = produkteService.getEinheiten();
}
return einheitList;
}
}
ProduktDatConverter.java:
#FacesConverter(forClass=Dat.class,value="produktDatConverter")
#ViewScoped
public class ProduktDatConverter implements Converter {
#Inject
#Transient DatenService datenService;
#Transient
protected final Logger logger = Logger.getLogger(getClass().getName());
// gets never called (of course)
public Object getAsObject(FacesContext arg0, UIComponent arg1, String str) {
logger.debug("getAsObject(): " + str);
return null;
}
public String getAsString(FacesContext arg0, UIComponent arg1, Object object) {
if (object instanceof Dat) {
// logger.debug(" returning id " + String.valueOf(((Dat) object).getId()));
return Long.toString(((Dat) object).getId());
}
return null;
}
}
Any ideas? Many thanks in advance!
You are using #ViewScoped along CDI Managed Bean. Change it to JSF Managed Bean. Better, use CODI instead
Also see:
View Scope in CDI Weld
CDI missing #ViewScoped and #FlashScoped
I suppose it's converter + injection problem. Please, click here and refer to the BalusC answer. You can also try to replace:
#FacesConverter(forClass=Dat.class,value="produktDatConverter")
#ViewScoped
with
#ManagedBean
#RequestScoped
and call the converter like this:
converter="#{produktDatConverter}"

Display edit Employee's profile using jsf

Using jsf I want to edit a employee profile, when user will click on any particular datatable row,then
I am able to get all that deatils of selected patient in an
arraylist. Now I want to set all the attritbutes in arraylist to
page1.xhtml backingbean , so When user will select a particular row,
he will navigate to page1.xhtml where he will get all these fields in
the form set already by arraylist attributes.
I am trying in this way.
> page1.xhtml
<h:outputLabel value="Name" />
<p:inputText id="name1" value="#{employeeBB.emp.name}" >
</p:inputText>
<h:outputLabel value="age" />
<p:inputText id="ag" value="#{employeeBB.emp.age}" >
</p:inputText>
<h:outputLabel value="code" />
<p:inputText id="code1" value="#{employeeBB.emp.code}" >
</p:inputText>
#ManagedBean(name = "employee")
#ViewScoped
public class emp {
private String name;
private String age;
private String code;
public String getName()
{ return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
#SessionScoped
#ManagedBean
public class EmployeeBB implements serializable{
private Employe emp;
public Employee getEmp() {
return emp;
}
public void setEmp(Employee emp) {
this.emp = emp;
}
}
#SessionScoped
#ManagedBean
public class AddEmployeeBB{
private ArrayList<Employee>empList;
private ArrayList<Employee>empList;
public ArrayList<Employee> getEmpList() {
if(empList==null){
empList=new ArrayList<Employee>();
}
return empList;
}
public void setEmpList(ArrayList<Employee> empList) {
this.empList = empList;
}
public void method() throws IOException{
String code='123';
EmployeeDAO obj=new EmployeeDAO(); // DAO class
empList=obj.getAllEmplInfo(code); // will get all needed information about employee of this code in this arrayist
for(int i=0;i<empList.size();i++){
String name=empList.get(i).getName();
String age=empList.get(i).getAge();
String code=empList.get(i).getCode();
Employee e=new Employee();
e.setName(name);
e.setAge(age);
e.setCode(code);
EmployeeBB obj1=new EmployeeBB();
obj1.setEmp(e); // now according to my logic object e will set to emp object of Employee, and
// that means all these values name ,agem and code will be set to my page1.xhtml and I will be able to see it.
}
}
But I am unable to get pag1.xhtml with filled values.
Show me the way.
The reason for it not being shown is that you are setting values in a object which you are creating
EmployeeBB obj1=new EmployeeBB();
obj1.setEmp(e);
JSF lifecycle doens't know about this object and everytime you are seeing blank.
In AddEmployeeBB add this
#ManagedProperty(value="employeeBB")
private EmployeeBB employeeBB = null; // create getter setter for this
then instead of this :
EmployeeBB obj1=new EmployeeBB();
obj1.setEmp(e);
Use this:
this.employeeBB.setEmp(e);

Resources