Multiple instances of a composite component in a view? - jsf-2

I read many similar questions, but I cannot figure out how to solve my problem:
I wrote a composite component backed by a view-scoped, self-contained managed bean.
The component consists of an autocomplete textbox with a button that opens a dialog. The user can select an item either by name (autocomplete) or selecting a node in a tree (dialog).
The backing bean implements all the stuff needed (data access, tree-logics etc) and should expose the selected item (as a POJO).
Now I have 2 problems:
Due to the complexity of the tree management, selectedObj property is accessed by a getter and a setter that do some stuff in the bean: they are not simply accessing a class field. Now I'm passing the entire bean as an attribute. How can I just make the bean's selectedObj the "value" attribute of my composite component?
How can I use multiple instance of my component in the same view?
Here is an example of the component:
<cc:interface>
<cc:attribute name="bean" type="com.yankee.OUTreeBean" required="true"/>
<cc:attribute name="listener" method-signature="void listener()"/>
</cc:interface>
<cc:implementation>
<p:dialog id="#{cc.id}_dialog" widgetVar="_dlg" header="Select OU" modal="true" dynamic="true" >
<p:toolbar>
<!-- some buttons to refresh, expand, collapse etc. -->
</p:toolbar>
<p:tree id="#{cc.id}_tree" value="#{cc.attrs.bean.root}" var="node"
selectionMode="single"
selection="#{cc.attrs.bean.selectedNode}">
<p:ajax event="select" update="#form" listener="#{cc.attrs.listener}" oncomplete="if (!args.validationFailed) _dlg.hide()" />
<p:treeNode>
<h:outputText value="#{node.OU_NAME}" />
</p:treeNode>
</p:tree>
</p:dialog>
<p:autoComplete id="#{cc.id}_inner" value="#{cc.attrs.bean.selectedObj}" completeMethod="#{cc.attrs.bean.completeObj}"
var="obj" itemLabel="#{obj.OU_NAME}" itemValue="#{obj}"
forceSelection="true"
converter="ouConverter"
multiple="false"
minQueryLength="2">
<p:ajax event="itemSelect" listener="#{cc.attrs.listener}" update="#form"/>
</p:autoComplete>
<div style="float: right">
<p:commandButton id="bSearch" icon="ui-icon-search" onclick="_dlg.show()"/>
</div>
</cc:implementation>
The backing bean of the COMPONENT:
#ManagedBean
#ViewScoped
public class OUTreeBean implements Serializable {
private static final long serialVersionUID = 1L;
private List<OU> data; // Data as plain list
protected TreeNode root; // root node of data as a tree
protected TreeNode selectedNode;
#PostConstruct
private void init() throws SQLException {
refreshData();
}
public OU getSelectedObj() {
if (selectedNode == null) {
return null;
}
return ((OU) selectedNode.getData());
}
public void setSelectedObj(OU ou) {
// Find the right tree node and do whatever needed
}
public TreeNode selectedNode getSelectedNode() {
// Blah blah
}
public void setSelectedNode(TreeNode selectedNode) {
// Blah blah
}
public List<OU> completeObj(String namePattern) {
// Autocomplete handler
}
public void refreshData() {
// Blah blah
}
// etc...
}
The using page excerpt:
<ism:selectOUTree id="cbSelectOu" bean="#{myBean.ouFilterBean}" listener="#{myBean.onOUChange}"/>
The backing bean of the PAGE:
#ManagedBean
#ViewScoped
public class MyBean implements Serializable {
private static final long serialVersionUID = 1L;
#ManagedProperty("#{oUTreeBean}")
private OUTreeBean ouFilterBean;
public void onOUChange() throws SQLException {
// Blah blah
}
}

I had the same problem few days ago. Just like you I used ManagedBean as an object that should do the job. After some time I figured out that I should just create FacesComponent. I'm new in JSF so it wasn't that easy to find the solution but it solved all my problems. This is how does it work:
view.xhtml
<h:body>
<cc:interface componentType="playerComponent">
<cc:attribute name="playerId" required="true"/>
</cc:interface>
<cc:implementation>
<c:set var="inplaceId" value="inplace-#{cc.attrs.playerId}" />
<c:set var="outputId" value="output-#{cc.attrs.playerId}" />
<h:form id="form-#{cc.attrs.playerId}">
<p:inplace editor="false" widgetVar="#{inplaceId}">
<h:inputText value="#{cc.player.name}" id="outputId"/>
<p:commandButton onclick="#{inplaceId}.save()" action="#{cc.save}" update="#{outputId}" value="save" />
<p:commandButton onclick="#{inplaceId}.cancel()" update="#{outputId}" value="cancel" />
</p:inplace>
</h:form>
</cc:implementation>
</h:body>
PlayerComponent.java
#FacesComponent("playerComponent")
public class PlayerComponent extends UINamingContainer {
private Player player;
private void init() {
Object idObj = getAttributes().get("playerId");
if (idObj != null) {
// create player object
}
}
public void save() {
// save player object
}
public Player getPlayer() {
if (player == null) {
init();
}
return player
}
}
Player.java (entity)
public class Player {
private name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
As I wrote I'm new in JSF and probably player object should be created in different way (using #PostConstruct on in constructor?) but this work.

Related

How do I transfer a value from one bean to another? (#ManagedProperty help)

I'm using PrimeFace4.0, JSF 2.2, Java, and XHTML. I have a string value that I need to pass from one bean to another. The value is selected on one XHTML page and when the user clicks to select, a new web page is launched. I can see from my server output that the value is successfully collected by the first bean (TestSelectBean), but I can't seem to get it into the next bean (TargetBeanFranz). When a String is hard coded into the bean, it works correctly. However, when I try to use the managed property to call it as per the user input, I get a NullPointer at the line of code (85) where I'm trying to use it.
The first HTML: testselect.xhtml
//irrelevant code
<p:layoutUnit position="center">
<h:form>
<p:selectOneRadio id="Test" value="#{testSelectBean.selectedNameOfExperiments}" >
<f:selectItems id="selectedNameOfExperiments" value="#{testSelectBean.nameofexperiments}" var="entry" itemValue="#{entry.key}" itemLabel="#{entry.value}" />
</p:selectOneRadio>
<p:commandLink id="nonAjax" action="open" actionListener="#{testSelectBean.getParameters}" style="margin-right:20px;" ajax="false">
<h:outputText value="Submit" />
</p:commandLink>
</h:form>
</p:layoutUnit>
The user chosen selectedNameOfExperiments is succsefully passed to TestSelectBean:
#ManagedBean(name = "testSelectBean", eager = true)
#SessionScoped
public class TestSelectBean implements Serializable {
private static final long serialVersionUID = 1L;
protected String selectedNameOfExperiments;
private final Map<String, String> nameofexperiments;
private transient String selected;
public TestSelectBean() throws SQLException {
selected = new String();
nameofexperiments = new HashMap<String, String>();
XYexpdataServiceAdapter xydata = new XYexpdataServiceAdapterImpl();
List<String> dbnameofexperiments = xydata.getNameofexperiments();
for (String ta : dbnameofexperiments) {
nameofexperiments.put(ta, ta);
}
}
public String getSelectedNameOfExperiments() {
return selectedNameOfExperiments;
}
public void setSelectedNameOfExperiments(String selectedNameOfExperiments1) {
this.selectedNameOfExperiments = selectedNameOfExperiments1;
}
public Map<String, String> getNameofexperiments() {
return nameofexperiments;
}
public void getParameters() {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("You've selected Experiment:" + selectedNameOfExperiments));
System.out.println("You've selected Experiment:" + selectedNameOfExperiments);
}
}
However, the XHTML FranzwPopup will not launch unless Experiment is hard coded into TargetBeanFranz.
//irrelevant code
<p:layoutUnit position="center">
<p:panelGrid columns="2" style=" font-size: 14">
<h:outputLabel value="Description" style="font-size: 20"/>
<h:outputText value="Neutron-induced production of protons, deuterons and tritons in Copper and Bismuth."/>
<h:outputLabel value="Reference" style="font-size: 20"/>
<h:outputText value="Nuclear Physics A510 (1990) 774-802 (Franz, et. al.)"/>
</p:panelGrid>
<form action="http://localhost:8080/primefaces/faces/handle.xhtml" method="get" name="login" onsubmit="process();
return false;" style="overflow-y: scroll">
Click to display chart data in a table.<br />
<input type="submit" value="Display"/>
</form>
<h:form>
<h:panelGrid columns="4" cellpadding="5">
<p:selectCheckboxMenu id="menu1" value="#{targetBeantFranz.selectedTargets}" label="Targets" filter="true" filterMatchMode="startsWith"
panelStyle="width:220px">
<f:selectItems value="#{targetBeantFranz.targets}" />
</p:selectCheckboxMenu>
<p:selectCheckboxMenu id="menu2" value="#{targetBeantFranz.selectedSecondaries}" label="Secondaries" filter="true" filterMatchMode="startsWith"
panelStyle="width:220px">
<f:selectItems value="#{targetBeantFranz.secondaries}" />
</p:selectCheckboxMenu>
<p:selectCheckboxMenu id="menu3" value="#{targetBeantFranz.selectedReactions}" label="Reactions" filter="true" filterMatchMode="startsWith"
panelStyle="width:220px">
<f:selectItems value="#{targetBeantFranz.reactions}" />
</p:selectCheckboxMenu>
<p:selectCheckboxMenu id="menu4" value="#{targetBeantFranz.selectedBeamEnergies}" label="Beam Energy" filter="true" filterMatchMode="startsWith"
panelStyle="width:220px">
<f:selectItems value="#{targetBeantFranz.beamenergies}" />
</p:selectCheckboxMenu>
</h:panelGrid>
<p:panel header="Resulting Plots">
<p:commandLink id="nonAjax" actionListener="#{targetBeantFranz.getParameters}" style="margin-right:20px;" ajax="false">
<button id="change">Submit Query</button>
</p:commandLink>
<div id="container" style="min-width: 310px; margin: 0 auto"></div>
</p:panel>
</h:form>
</p:layoutUnit>
//more irrelevant
Instead, I get a NPE on line 85 of TargetBeanFranz where I try to initialize Experiment.
//stuff
#ManagedBean(name = "targetBeantFranz", eager = true)
#SessionScoped
public class TargetBeanFranz implements Serializable {
#ManagedProperty(value="#{testSelectBean}")
private TestSelectBean testSelectBean;
public TestSelectBean getTestSelectBean(){
return testSelectBean;
}
public void setTestSelectBean (TestSelectBean testSelectBean) {
this.testSelectBean = testSelectBean;
}
private static final long serialVersionUID = 1L;
private String Experiment;
//more stuff
public TargetBeanFranz() throws SQLException {
targets = new HashMap<String, String>();
selectedTargets = new ArrayList<String>();
secondaries = new HashMap<String, String>();
selectedSecondaries = new ArrayList<String>();
reactions = new HashMap<String, String>();
selectedReactions = new ArrayList<String>();
beamenergies = new HashMap<String, String>();
selectedBeamEnergies = new ArrayList<String>();
seriesDataArray = new ArrayList<String>();
seriesDataArArray = new ArrayList<ArrayList<String>>();
seriesNameArray = new ArrayList<String>();
seriesToolTipArray = new ArrayList<String>();
seriesErrorArray = new ArrayList<String>();
seriesErrorArArray = new ArrayList<ArrayList<String>>();
allselected = new ArrayList<XYexpdata>();
Experiment = testSelectBean.getSelectedNameOfExperiments(); //This is line 85 and where I'm getting the NPE.
XYexpdataServiceAdapter xydata = new XYexpdataServiceAdapterImpl();
List<String> dbtargets = xydata.getTargetNamesbyExperiment(Experiment);
for (String ta : dbtargets) {
targets.put(ta, ta);
}
List<String> dbsecondaries = xydata.getSecondaryNamesbyExperiment(Experiment);
for (String ta : dbsecondaries) {
secondaries.put(ta, ta);
}
List<String> dbreactions = xydata.getReactionNamesbyExperiment(Experiment);
for (String ta : dbreactions) {
reactions.put(ta, ta);
}
List<String> dbbeamenergies = xydata.getBeamEnergybyExperiment(Experiment);
for (String ta : dbbeamenergies) {
beamenergies.put(ta, ta);
}
}
//more code
I think the reason for your problem is, that the signature of the action listener in your command link does not match.
The method in the bean has to be of the following type:
public void getParameters(javax.faces.ActionEvent event) {
.....
}
If you are using Java EE 7 you should also avoid the annotations in the package javax.faces.bean, since they are abandoned. Use CDI beans, it makes life easier:
#Named
#SessionScoped
public class TestSelectBean {
...
}
#Named
#SessionScoped
public class TargetBeanFranz {
#Inject
private TestSelectBean testSelectBean;
....
}
See more about the CDI topic here: Backing beans (#ManagedBean) or CDI Beans (#Named)?

jsf2 template component with parameter

I have a jsf template page. In this template i would like to use component with parameter. Actually i would like to iterate over collection. This collection should be determined by parameters from particular page. Parameter name is selectedMenu. How can i use this in my bean?
<div id="notes">
selectedMenu: #{selectedMenu}
<h:form>
<h:inputHidden value="#{notatkaController.searchForm.kategoria}">
<f:param value="#{selectedMenu}" />
</h:inputHidden>
<ui:repeat value="#{notatkaController.items}" var="item" varStatus="iter">
<f:param value="#{selectedMenu}" />
<p class="q-title"><strong><h:outputText value="#{item.ntaData}" /></strong></p>
<p class="answer"><h:outputText value="#{item.ntaDane}" escape="false" /></p>
</ui:repeat>
<span>Moje notatki</span>
<textarea>Tu wpisz treść swojej notatki</textarea>
<span>[+] dodaj notatkę</span>
</h:form>
</div>
My bean:
#ManagedBean(name = "notatkaController")
#ViewScoped
public class NotatkaController extends AbstractController<Notatka> implements Serializable {
#EJB
private pl.alfaprojekt.model.session.NotatkaFacade ejbFacade;
private NotatkaSearchForm searchForm;
public DataModel getItems() {
if (items == null)
items = getPagination().createPageDataModel();
return items;
}
public PaginationHelper getPagination() {
if (pagination == null) {
if (paginationSize == null)
paginationSize = 10;
pagination = new PaginationHelper(paginationSize) {
#Override
public int getItemsCount() {
return getFacade().countByParam(getSearchForm());
}
#Override
public DataModel createPageDataModel() {
if (rapId == null)
return new ListDataModel(getFacade().findRangeByParam(getSearchForm(), new int[]{getPageFirstItem(), getPageFirstItem() + getPageSize()}));
else {
Long uzyId = SessionUtil.getUser().getUzyId();
return new ListDataModel(convertToRaportWierszList(getFacade().findRangeByParam(getSearchForm(), new int[]{getPageFirstItem(), getPageFirstItem() + getPageSize()}), uzyId));
}
}
};
}
return pagination;
}
}
I am not sure I understood the question but the question I answer is this:
I want to dynamically display something. I want to tell it what to display when the page gets accessed.
View:
You could then use the following:
<f:event listener="#{myBean.myMethod}" type="preRenderView" />
Bean:
public void myMethod(ComponentSystemEvent event){
//your logic here
}
With parameter:
<f:metadata>
<f:event listener="#{myBean.myMethod}" type="preRenderView" id="fevent"/>
<f:attribute name="myParam" value="#{mySecondBean.param)" />
</f:metadata>
And
public void myMethod(ComponentSystemEvent event){
String id = (String) event.getComponent().getAttributes().get("myParam");
}

resetValue in custom UIInput Composite Component not working

I have a custom UIInput wrapped in a composite component like this:
<cc:interface componentType="SingleUpload">
<cc:attribute name="value" required="true" />
...
</cc:interface>
<cc:implementation>
...
<p:fileUpload id="fileUpload" update="#form" auto="true"
fileUploadListener="#{cc.fileUploaded}" rendered="#{cc.attrs.value == null}"/>
...
<h:commandButton rendered="#{cc.attrs.value != null}" action="#{cc.removeFile}">
<p:ajax execute="#this" update="#form" />
</h:commandButton>
...
</cc:implementation>
The backing component looks like this:
#FacesComponent(value = "SingleUpload")
public class SingleUpload extends UIInput implements NamingContainer, Serializable {
/** */
private static final long serialVersionUID = 2656683544308862007L;
#Override
public String getFamily() {
return "javax.faces.NamingContainer";
}
public void fileUploaded(FileUploadEvent event) throws IOException {
FileData file = new FileData();
file.setContentMimeType(MimeTypeUtils.getContentType(event.getFile().getContentType(), event.getFile().getFileName()));
file.setInputStream(event.getFile().getInputstream());
file.setName(FilenameUtils.getName(event.getFile().getFileName()));
setValue(file);
}
public void removeFile() {
resetValue();
// value should be null now, but it is not, why??
FileData value=(FileData) getValue();
}
}
It is used this way:
<ki:singleUpload value="#{fileModel.file}" title="File Upload" />
So when the action removeFile is called, i want to set the value to null. This works, when I follow these steps:
load a page containing a singleUpload component with no initial value (fileModel.file == null)
upload a file, so value in SingleUpload is not null anymore
remove the file
But when I do the following
load a page containing a singleUpload component with an initial value (fileModel.file != null)
remove the intial value (click on button, removeFile is called)
=> removing the value from the component does not seem to be possible. Why??

How to pass attributes to a composite-component

I am having trouble in using a JSF composite-component in the right way. I put some components together and everything was working. Then I just extracted the code to a composite-component, and passed the corresponding attributes and suddenly I am getting conversation exceptions.
<composite:interface>
<composite:attribute name="selectedIDs" type="java.util.Collection" required="true"/>
<composite:attribute name="selectItems" type="java.util.List" required="true" />
<composite:attribute name="addAction" required="true"/>
<composite:attribute name="deleteAction" required="true"/>
<composite:attribute name="deleteButtonDisabled" />
<composite:attribute name="ajaxListener" method-signature="void listener(javax.faces.event.AjaxBehaviorEvent)"/>
</composite:interface>
<composite:implementation>
<div class="myClass">
<h:outputStylesheet library="views" name="selectManyControlPanel.css" target="head" />
<h:form>
<h:selectManyListbox value="#{cc.attrs.selectedIDs}">
<f:selectItems value="#{cc.attrs.selectItems}" />
<f:ajax render="delete"
listener="#{cc.attrs.ajaxListener}" />
</h:selectManyListbox>
<br />
<h:commandButton id="delete" value="Delete"
disabled="#{cc.attrs.deleteButtonDisabled}"
action="#{cc.attrs.deleteAction}" />
<h:commandButton id="add" value="Add" action="#{cc.attrs.addAction}"/>
</h:form>
</div>
</composite:implementation>
Here is the page where I am using the newly created component
<view:selectManyControlPanel
selectedIDs="#{bean.selectedIds}"
selectItems="#{bean.listOfSelectItems}"
addAction="#{bean.addNew}"
deleteAction="#{bean.deleteSelection}"
ajaxListener="#{bean.selectionChanged}"
deleteButtonDisabled="#{bean.deleteButtonDisabled}" />
Bean (some methods skipped an parts renamed)
package views;
#SuppressWarnings("serial")
#Named
#RequestScoped
public class Bean implements Serializable, IOverviewView {
#Inject
Presenter presenter;
private boolean deleteButtonDisabled;
private List<SelectItem> listOfSelectItems;
private Set<Long> selectedIds;
public Bean(){
deleteButtonDisabled = true;
listOfSelectItems = new ArrayList<SelectItem>(10);
}
public void selectionChanged(AjaxBehaviorEvent event){
if(selectedIds.isEmpty())
deleteButtonDisabled = true;
else
deleteButtonDisabled = false;
}
public void deleteBikes(){
presenter.delete(selectedIds);
}
public void addNew(){
presenter.addNew();
}
public List<SelectItem> getListOfSelectItems() {
return listOfSelectItems;
}
public Set<Long> getSelectedIds() {
return selectedIds;
}
#PostConstruct
public void init(){
System.out.println("INITIALIZING BEAN: " + this.getClass().getName());
this.presenter.setView(this);
this.presenter.init();
}
public boolean isDeleteButtonDisabled() {
return deleteButtonDisabled;
}
#Override
public void setDeleteButtonEnabled(boolean isEnabled) {
deleteButtonDisabled = !isEnabled;
}
public void setListOfSelectItems(List<SelectItem> list) {
this.listOfSelectItems = list;
}
public void setSelectedIds(Set<Long> selectedIds) {
this.selectedIds = selectedIds;
}
#Override
public void updateBikesList(Set<ViewObject> objectsToDisplay) {
updateList(objectsToDisplay);
}
private void updateList(Set<ViewObject> objectsToDisplay){
listOfSelectItems.clear();
for (ViewObject vO : objectsToDisplay) {
final String label = vO.getManufacturer() + ", " + vO.getModel() + " (" + vO.getYear() + ")";
listOfSelectItems.add(new SelectItem(vO.getId(), label));
}
}
....
}
Exception
javax.el.ELException: /resources/views/selectManyControlPanel.xhtml #25,56 value="#{cc.attrs.selectedIDs}": /index.xhtml #21,70 selectedIDs="#{bean.selectedIds}": Cannot convert [Ljava.lang.String;#1e92093 of type class [Ljava.lang.String; to interface java.util.Set
The only thing that changed is that I am using the composition instead of the plain code. The EL-expressions are still the same. Can someone enlighten me please? I expected that the parameters are just passed through but obviously not...
This is related to Mojarra issue 2047. It's scheduled to be fixed in the upcoming 2.2.
The issue ticket also proposes the following workaround:
<view:selectManyControlPanel
selectedIDsBean="#{bean}"
selectedIDsProperty="selectedIds"
with in CC interface
<composite:attribute name="selectedIDsBean" required="true"/>
<composite:attribute name="selectedIDsProperty" required="true"/>
and in CC implementation
<h:selectManyListbox value="#{cc.attrs.selectedIDsBean[cc.attrs.selectedIDsProperty]}">

How to highlight a primefaces tree node from backing bean

I am working with primefaces tree component. There is a context menu for the tree (add a node, edit node, delete node). After performing some operation, I need to refresh the tree and then highlight the node added or edited.
This is my code.
index.xhtml
<p:treeNode>
<h:outputText value="#{node}" />
</p:treeNode>
</p:tree>
<p:contextMenu for="pTree" id="cmenu">
<p:menuitem value="Add topic as child" update="pTree, cmenu"
actionListener="#{treeBean.addChildNode}" />
<p:menuitem value="Add topic Below" update="pTree, cmenu"
actionListener="#{treeBean.addTopicBelow}" />
<p:menuitem value="Delete Topic" update="pTree, cmenu"
actionListener="#{treeBean.deleteNode}" />
</p:contextMenu>
treeBean.java
public class TreeBean implements Serializable {
private TreeNode root;
public TreeBean() {
root = new DefaultTreeNode("Root", null);
// GET the root nodes first L0
List<TracPojo> rootNodes = SearchDao.getRootNodes111();
Iterator it = rootNodes.iterator();
while (it.hasNext()) {
TracPojo t1 = (TracPojo) it.next();
String tid = t1.getTopicID();
TreeNode node1 = new DefaultTreeNode(t1, root);
}
}
public TreeNode getRoot() {
return root;
}
public void addChildNode(ActionEvent actionEvent)
{
List record = NewSearchDao.getRecord(selectedNode);
Iterator it = record.iterator();
while (it.hasNext()) {
Object[] record1 = (Object[]) it.next();
setParentID_dlg((String) record1[0]);
setSortIndex((Integer) record1[2]);
}
}
public void saveChilddNode() {
System.out.println("Save as Child Node ........");
}
}
Primefaces p:treeNode has an attribute styleClass. You could set this dynamically from your backing bean. The view would look like:
<p:tree>
<p:treeNode styleClass="#{treeBean.styleClass}">
<h:outputText value="#{node}" />
</p:treeNode>
</p:tree>
Then add a member styleClass to your TreeBean with get/set method that returns a string representing the style class:
public class TreeBean implements Serializable {
private String styleClass;
...
public String getStyleClass() {
// your style selection logic here
}
...
}
Don't forget to add the style classes to your css.
Unless you set the selectedNode, which you declare as selection="#{treeBean.selectedNode}", to null, it is already selected and the only thing you have to do is to update the tree component from the triggering component; in your case it is:
<p:menuitem update=":yourForm:pTree" /*rest of the stuff*/ />

Resources