How can I show/hide component with JSF? - java

How can I show/hide component with JSF?
I am currently trying so do the same with the help of javascript but not successfull.
I cannot use any third party libraries.
Thanks| Abhi

You can actually accomplish this without JavaScript, using only JSF's rendered attribute, by enclosing the elements to be shown/hidden in a component that can itself be re-rendered, such as a panelGroup, at least in JSF2. For example, the following JSF code shows or hides one or both of two dropdown lists depending on the value of a third. An AJAX event is used to update the display:
<h:selectOneMenu value="#{workflowProcEditBean.performedBy}">
<f:selectItem itemValue="O" itemLabel="Originator" />
<f:selectItem itemValue="R" itemLabel="Role" />
<f:selectItem itemValue="E" itemLabel="Employee" />
<f:ajax event="change" execute="#this" render="perfbyselection" />
</h:selectOneMenu>
<h:panelGroup id="perfbyselection">
<h:selectOneMenu id="performedbyroleid" value="#{workflowProcEditBean.performedByRoleID}"
rendered="#{workflowProcEditBean.performedBy eq 'R'}">
<f:selectItem itemLabel="- Choose One -" itemValue="" />
<f:selectItems value="#{workflowProcEditBean.roles}" />
</h:selectOneMenu>
<h:selectOneMenu id="performedbyempid" value="#{workflowProcEditBean.performedByEmpID}"
rendered="#{workflowProcEditBean.performedBy eq 'E'}">
<f:selectItem itemLabel="- Choose One -" itemValue="" />
<f:selectItems value="#{workflowProcEditBean.employees}" />
</h:selectOneMenu>
</h:panelGroup>

Generally, you need to get a handle to the control via its clientId. This example uses a JSF2 Facelets view with a request-scope binding to get a handle to the other control:
<?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">
<h:head><title>Show/Hide</title></h:head>
<h:body>
<h:form>
<h:button value="toggle"
onclick="toggle('#{requestScope.foo.clientId}'); return false;" />
<h:inputText binding="#{requestScope.foo}" id="x" style="display: block" />
</h:form>
<script type="text/javascript">
function toggle(id) {
var element = document.getElementById(id);
if(element.style.display == 'block') {
element.style.display = 'none';
} else {
element.style.display = 'block'
}
}
</script>
</h:body>
</html>
Exactly how you do this will depend on the version of JSF you're working on. See this blog post for older JSF versions: JSF: working with component identifiers.

Use the "rendered" attribute available on most if not all tags in the h-namespace.
<h:outputText value="Hi George" rendered="#{Person.name == 'George'}" />

You should use <h:panelGroup ...> tag with attribute rendered. If you set true to rendered, the content of <h:panelGroup ...> won't be shown. Your XHTML file should have something like this:
<h:panelGroup rendered="#{userBean.showPassword}">
<h:outputText id="password" value="#{userBean.password}"/>
</h:panelGroup>
UserBean.java:
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class UserBean implements Serializable{
private boolean showPassword = false;
private String password = "";
public boolean isShowPassword(){
return showPassword;
}
public void setPassword(password){
this.password = password;
}
public String getPassword(){
return this.password;
}
}

One obvious solution would be to use javascript (which is not JSF). To implement this by JSF you should use AJAX. In this example, I use a radio button group to show and hide two set of components. In the back bean, I define a boolean switch.
private boolean switchComponents;
public boolean isSwitchComponents() {
return switchComponents;
}
public void setSwitchComponents(boolean switchComponents) {
this.switchComponents = switchComponents;
}
When the switch is true, one set of components will be shown and when it is false the other set will be shown.
<h:selectOneRadio value="#{backbean.switchValue}">
<f:selectItem itemLabel="showComponentSetOne" itemValue='true'/>
<f:selectItem itemLabel="showComponentSetTwo" itemValue='false'/>
<f:ajax event="change" execute="#this" render="componentsRoot"/>
</h:selectOneRadio>
<H:panelGroup id="componentsRoot">
<h:panelGroup rendered="#{backbean.switchValue}">
<!--switchValue to be shown on switch value == true-->
</h:panelGroup>
<h:panelGroup rendered="#{!backbean.switchValue}">
<!--switchValue to be shown on switch value == false-->
</h:panelGroup>
</H:panelGroup>
Note: on the ajax event we render components root. because components which are not rendered in the first place can't be re-rendered on the ajax event.
Also, note that if the "componentsRoot" and radio buttons are under different component hierarchy. you should reference it from the root (form root).

check this below code.
this is for dropdown menu. In this if we select others then the text box will show otherwise text box will hide.
function show_txt(arg,arg1)
{
if(document.getElementById(arg).value=='other')
{
document.getElementById(arg1).style.display="block";
document.getElementById(arg).style.display="none";
}
else
{
document.getElementById(arg).style.display="block";
document.getElementById(arg1).style.display="none";
}
}
The HTML code here :
<select id="arg" onChange="show_txt('arg','arg1');">
<option>yes</option>
<option>No</option>
<option>Other</option>
</select>
<input type="text" id="arg1" style="display:none;">
or you can check this link
click here

Related

Best Practice for submitting a JSF 2 form that might use ajax if we have an error response from the submit

I have a submit button that submits to a database using the button below.
If this button return "error", I want to use an AJAX to remove the form and its content and add a text in place of the form.
Edit:
The reason of staying in the same page after clicking the submit button is because, the same CMS content above and below the form is used on both the current page and redirected page after submit. Thus, its easier to just remove form and display the error page. This will avoid the whole page to reload due to redirect.
</h:form id="myForm">
...
<h:commandButton action="#{testBean.submitForm}"
type="submit" value="Create Button">
</h:commandButton>
</h:form>
Remember to use <h:head> and <h:body> instead of <head> and <body>.
<h:panelGroup id="mypanelgroup">
<h:outputText value="#{testBean.errorMessage}" rendered="#{testBean.errorMessage ne null}" />
<h:form rendered="#{testBean.errorMessage eq null}">
<!-- -->
<h:commandButton value="submit" action="#{testBean.submitForm()}">
<f:ajax execute="#form" render=":mypanelgroup"></f:ajax>
</h:commandButton>
</h:form>
</h:panelGroup>
#ManagedBean
public class TestBean {
private String errorMessage = null;
public String getErrorMessage() {
return errorMessage;
}
public void submitForm(){
// .....
//if(error){
this.errorMessage = "My error Message";
//}
}
}
Initially, the <h:outputText> is not rendered because errorMessage by default was set to null.

Partial Page Rendering (primefaces) need two times click before changing

I'm trying to apply this answer to rendering a part of my primefaces page but the problem is that I need to click twice before getting the part changed is there a solution ?
this is my code
index.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
</h:head>
<h:body>
<h:panelGroup id="header" layout="block">
<h1>Header</h1>
</h:panelGroup>
<h:panelGroup id="menu" layout="block">
<h:form>
<f:ajax render=":content">
<p:commandButton value="next" action="#{navBean.next}" />
<p:commandButton value="back" action="#{navBean.back}" />
</f:ajax>
</h:form>
</h:panelGroup>
<h:panelGroup id="content" layout="block">
<h:panelGroup rendered="#{navBean.activePage == 'firstAjax'}">
<ui:include src="firstAjax.xhtml" />
</h:panelGroup>
<h:panelGroup rendered="#{navBean.activePage == 'lastAjax'}">
<ui:include src="lastAjax.xhtml" />
</h:panelGroup>
</h:panelGroup>
</h:body>
</html>
and this is my managedBean
package com.project.beans;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean(name = "navBean")
#SessionScoped
public class NavBean {
private String activePage = "firstAjax";
public String getActivePage() {
return activePage;
}
public void setActivePage(String activePage) {
this.activePage = activePage;
}
public void next() {
System.out.println("next in " + activePage);
this.setActivePage("lastAjax");
System.out.println("next out " + activePage);
}
public void back() {
System.out.println("back in " + activePage);
this.setActivePage("firstAjax");
System.out.println("back out " + activePage);
}
}
the firstAjax.xhtml and the lastAjax.xhtml is nearly the same
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:form>
<h2>content first</h2>
</h:form>
</ui:composition>
Your page is not working because PrimeFaces has its own ajax engine. You have to use it when using p:commandButton. You don't need to use p:ajax tag, because p:commandButton has ajax behaviour activated by default, just use the update attribute (equivalent to the render attribute of f:ajax component):
<h:form>
<p:commandButton value="next" action="#{navBean.next}" update=":content"/>
<p:commandButton value="back" action="#{navBean.back}" update=":content"/>
</h:form>
If you want to stick to plain jsf use this instead (it looks much more like the example by BalusC you mentioned):
<h:form>
<f:ajax render=":content" >
<h:commandButton value="next" action="#{navBean.next}" />
<h:commandButton value="back" action="#{navBean.back}" />
</f:ajax>
</h:form>
As a final note, it must be said that p:ajax tag (at least in PrimeFaces 4.0) doesn't support the syntax we are using here, outside the p:commandButton components, because it's not able to determine the event attribute and therefore it throws: <p:ajax> Event attribute could not be determined: null

Primefaces component does not update

I have a p:dataGrid that used to update itself in 3.0.1. Now I upgraded to PF 3.1 and the ajax update event of the "availableIcons" component does not fire anymore. I don't get an error that the component is not found in the view.
The XHMTL
<h:form id="Application">
......
<p:confirmDialog id="iconDialog" message="Select one icon"
showEffect="bounce" hideEffect="explode" header="Icon Selection"
severity="alert" widgetVar="iconSelect" modal="false">
<p:dataGrid id="availableIcons" var="icon"
value="#{appEditController.availableIcons}" columns="4">
<p:column>
<p:panel id="pnl" header="" style="text-align:center">
<h:panelGrid columns="1" style="width:100%" id="iconPanelGrid">
<p:graphicImage value="/resources/icons/#{icon.icon}"
id="iconImage" />
<p:selectBooleanCheckbox id="iconSelector"
value="#{icon.selected}"
disabled="#{appEditController.isIconSelected(icon)}">
<p:ajax update="availableIcons" event="change"
process="availableIcons"
listener="#{appEditController.iconSelectedChanged(icon)}" />
</p:selectBooleanCheckbox>
</h:panelGrid>
</p:panel>
</p:column>
</p:dataGrid>
<p:commandButton value="Done" update="currentIcon"
action="#{appEditController.updateCurrentIcon}" ajax="false"
oncomplete="iconSelect.hide()" />
</p:confirmDialog>
.......
</h:form>
I don't see what's missing or what's incorrect.
This is the backing bean code
public void updateCurrentIcon() {
for (IconVO iconVO : availableIcons) {
if (iconVO.isSelected()) {
log.debug("CURRENT ICON IS NOW " + iconVO.getIcon());
currentIcon = iconVO;
break;
}
}
}
public void iconSelectedChanged(IconVO iconVO) {
if (iconVO == currentIcon) {
log.debug("NULLING ICON");
currentIcon = null;
} else {
log.debug("SETTING NEW ICON");
currentIcon = iconVO;
}
}
public boolean isIconSelected(IconVO iconVO) {
log.debug("IS ICON SELECTED " + iconVO.getIcon());
if (currentIcon == null
|| iconVO.getIcon().equals(currentIcon.getIcon())) {
return false;
}
return currentIcon != null;
}
I tried to do update="#form", then the update fires but it closes the modal panel completely.
Thanks,
Coen
Indeed, the way how PrimeFaces locates components by relative client ID has been changed in PrimeFaces 3.1 to adhere the UIComponent#findComponent() javadoc.
In your particular case, you need to specify the absolute client ID of the <p:dataGrid> instead. Easiest way to figure it is to check the ID of the <p:dataGrid> in the generated HTML source. With the code given so far, that would be Application:availableIcons. You need to prefix it with : to make it absolute and then reference it in update as follows:
<p:ajax update=":Application:availableIcons" ... />
Update as per the comments it turns out to not work at all. You could try wrapping the table in some invisible containing component like <h:panelGroup> and update it instead. Alternatively, you could consider moving the <h:form> into the dialog and use update="#form" instead. Having the <h:form> outside the dialog is kind of odd anyway. You surely won't submit all the other inputs which are outside the dialog inside the same form.

JSF2.0 Ajax Navigation Pattern

Would really appreciate some guidance concerning patterns for a Web App using JSF 2.0, PrimeFaces and Ajax. Our current system uses JSPs with standard submits and we have 1 JSP for each functional page in our app. Each JSP calls an action class for logic, navigation and EJB calls. Migrating to JSF using the same set up would be relatively simple i.e. 1 xhtml page and associated backing bean and navigation/logic done via a do method. However we want to submit via Ajax and this causes puzzles in my head. If I load abc1.xhtml and submit with ajax then I stay on abc1.xhtml although I might need to go to abc2.xhtml. I thought about having the associated forms on 1 xhtml page and using the rendered property to decide what to display. This works but I'm not comfortable having lots of forms in 1 page. Ideally I want to keep each page separate but don't know how this is possible with Ajax. Any ideas would be much appreciated.
Edit - This was original Solution but now has been refined in my answer below. This works but there seems to be issues with the Params. When I click on the AAA or BBB link I need to pass a param so that the ViewController bean knows what was clicked and where to set the Destination page. However if I click the Submit button in AAA.xhtml the content doesn't change unless I also add <f:param name="tranID" value="AAA"/> to the command button. I'd thought I'd handled a null Param in the ViewController constructor but obviously I'm missing something important. My only thoughts were to do with the URL. When I click the menu link it adds the param on to the url ?tranID=AAA. If I then don't add the param onto the subsequent submit is this effectively changing the url and causing some sort of mismatch?
viewController.xhtml
<h:body>
<h:panelGroup layout="block" id="transactionControl">
<h4>
<h:outputLink id="mfa" value="#{facesContext.externalContext.requestContextPath}/xhtml/viewController.xhtml" styleClass="menuLink">
<h:outputText value="AAA"></h:outputText>
<f:param name="tranID" value="AAA"/>
</h:outputLink>
</h4>
<h4>
<h:outputLink id="inq" value="#{facesContext.externalContext.requestContextPath}/xhtml/viewController.xhtml" styleClass="menuLink">
<h:outputText value="BBB"></h:outputText>
<f:param name="tranID" value="BBB"/>
</h:outputLink>
</h4>
</h:panelGroup>
<h:panelGroup layout="block" id="content" style="border-style: solid;">
<ui:include src="#{viewController.destinationPage}.xhtml"></ui:include>
</h:panelGroup>
</h:body>
AAA.xhtml
<h:body>
<h:form prependId="false">
<h:outputText value="Click the button to go to AAB"></h:outputText>
<p>
<p:commandButton id="submitButton" value="Go" ajax="true" actionListener="#{viewController.doAAAtoAAB}"
process="#form"
update="content">
<f:param name="tranID" value="AAA"/>
</p:commandButton>
</p>
</h:form>
</h:body>
AAB.xhtml
<h:body>
<h:panelGroup layout="block" id="subContent">
<h:outputText value="This is the AAB content"></h:outputText>
</h:panelGroup>
</h:body>
BBB.xhtml and BBC.xhtml as above
ViewController bean
package com.mcpplc.supportclient.webapp.managedBeans;
import javax.faces.context.FacesContext;
import java.io.Serializable;
#ManagedBean
#ViewScoped
public class ViewController implements Serializable
{
String destinationPage = "splash";
FacesContext context;
String callingTranID;
public ViewController ()
{
context = FacesContext.getCurrentInstance();
callingTranID = context.getExternalContext().getRequestParameterMap().get("tranID");
if (callingTranID != null )
{
destinationPage = callingTranID;
}
}
public void doAAAtoAAB()
{
destinationPage = "AAB";
}
public void doBBBtoBBC()
{
destinationPage = "BBC";
}
public String getDestinationPage()
{
return destinationPage;
}
public void setDestinationPage( String destinationPage )
{
this.destinationPage = destinationPage;
}
}
My recomendation for you is, do not use ajax unless you need it. If you need to actually call another page after a request, what is the point in using ajax?
Try to think in the application workflow and apply ajax where it is necessary. The cases you think that ajax will be confusing, is because you probably should not be using it
Cheers
Decided to answer my own question as I have now have a full working ajax only prototype app. This fits my initial requirements of having each view saved as one .xhtml page and each view having it's own backing bean. Instead of further polluting my question here is the simplified code. (Thanks to BalusC for his previous answer that really helped although its now disappeared).
viewController.xhtml
<h:body>
<h:panelGroup layout="block" id="transactionControl">
<h:form>
<h4>
<p:commandLink id="mfa" ajax="true" process="#form" update="content transactionBannerContent" styleClass="menuLink" oncomplete="showTransaction01Screens();">
<h:outputText value="MFA"></h:outputText>
<f:setPropertyActionListener target="#{viewController.destinationPage}" value="MFA01"/>
</p:commandLink>
</h4>
<h4>
<p:commandLink id="inq" ajax="true" process="#form" update="content transactionBannerContent" styleClass="menuLink" oncomplete="showTransaction01Screens();">
<h:outputText value="INQ"></h:outputText>
<f:setPropertyActionListener target="#{viewController.destinationPage}" value="INQ01"/>
</p:commandLink>
</h4>
</h:form>
</h:panelGroup>
<h:panelGroup layout="block" id="content" style="border-style: solid;">
<ui:include src="#{viewController.destinationPage}.xhtml"></ui:include>
</h:panelGroup>
</h:body>
mfa01.xhtml
<h:panelGroup layout="block" id="mfa01">
<h:form id="mfa01Form">
<p>
<span>
<h:outputLabel value="UCN" for="ucn"/>
<h:inputText id="ucn" value="#{mfa01BackingBean.mfa01FormVO.ucn}" size="20"/>
</span>
</p>
<p class="submitButton">
<p:commandButton id="submitButton" value="Go" ajax="true" actionListener="#{mfa01BackingBean.doMFA01}" process="#form"
update="content transactionBannerContent" oncomplete="ajaxFinished('MFA01')">
</p:commandButton>
</p>
</h:form>
</h:panelGroup>
mfa02.xhtml
<h:panelGroup layout="block" id="mfa02">
<h:form id="mfa02Form" prependId="true">
<p>
<span style="width:25%">
<h:outputLabel value="Vessel Name"/>
<h:outputText id="vesselName"
value="#{mfa02BackingBean.mfa02FormVO.vesselName}"/>
</span>
<span style="width:75%">
<h:outputLabel value="ETA"/>
<h:outputText id="eta"
value="#{mfa02BackingBean.mfa02FormVO.eta}"/>
</span>
</p>
<p>
<span>
<h:outputLabel value="Unit ID" for="unitID"/>
<h:inputText id="unitID"
value="#{mfa02BackingBean.mfa02FormVO.unitID}"
size="20"
required="true" validator="#{mfa02BackingBean.validateData}"/>
</span>
</p>
<p class="submitButton">
<p:commandButton id="submitButton" value="Go" ajax="true" action="#{mfa02BackingBean.doMFA02}"
process="#form" update="content transactionBannerContent" oncomplete="ajaxFinished('MFA02')">
<f:param name="randomString" value="AAA"/>
</p:commandButton>
</p>
</h:form>
</h:panelGroup>
ViewController.java
package com.mcpplc.supportclient.webapp.managedBeans;
import javax.faces.bean.*;
import java.io.Serializable;
#ManagedBean (name = "viewController")
#SessionScoped
public class ViewController implements Serializable
{
String destinationPage = "splash";
String transactionID;
public String getDestinationPage()
{
return destinationPage;
}
public void setDestinationPage( String destinationPage )
{
this.destinationPage = destinationPage;
transactionID = destinationPage.toUpperCase();
}
public String getTransactionID()
{
return transactionID;
}
public void setTransactionID( String transactionID )
{
this.transactionID = transactionID;
}
}
mfa01BackingBean.java
package com.mcpplc.supportclient.webapp.managedBeans;
import com.mcpplc.supportclient.webapp.Utils.JSFUtils;
import com.mcpplc.supportclient.webapp.valueObjects.*;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.RequestScoped;
#ManagedBean
#RequestScoped
public class Mfa01BackingBean
{
#ManagedProperty(value = "#{viewController}")
private ViewController viewController;
private ImportConsignmentVO importConsignmentVO;
private VoyageVO voyageVO;
private Mfa01FormVO mfa01FormVO;
private Mfa02FormVO mfa02FormVO;
#PostConstruct
public void init()
{
mfa01FormVO = new Mfa01FormVO();
}
public void doMFA01()
{
//pretend to get VOs
importConsignmentVO = new ImportConsignmentVO();
voyageVO = new VoyageVO();
//set some VO stuff
if ( mfa01FormVO.getUcn().equalsIgnoreCase( "123" ) )
{
importConsignmentVO.setUnitID("AJF1");
voyageVO.setVesselName("Ever Glade");
}
else {
importConsignmentVO.setUnitID("ZZZ1");
voyageVO.setVesselName("Ever Champion");
}
importConsignmentVO.setType("41G1");
importConsignmentVO.setWeight("25000");
voyageVO.setEta("01/01/2011");
constructMFA02Form();
viewController.setDestinationPage("mfa02");
}
private void constructMFA02Form()
{
mfa02FormVO = new Mfa02FormVO();
mfa02FormVO.setUnitID(importConsignmentVO.getUnitID());
mfa02FormVO.setType(importConsignmentVO.getType());
mfa02FormVO.setWeight(importConsignmentVO.getWeight());
mfa02FormVO.setMfaRef("12345");
mfa02FormVO.setVesselName(voyageVO.getVesselName());
mfa02FormVO.setEta(voyageVO.getEta());
JSFUtils.addObjectToRequest(Mfa02FormVO.class.getName(), mfa02FormVO);
}
.....getters&setters
}
mfa02BackingBean.java
package com.mcpplc.supportclient.webapp.managedBeans;
import com.mcpplc.supportclient.webapp.Utils.JSFUtils;
import com.mcpplc.supportclient.webapp.valueObjects.*;
import java.io.Serializable;
import java.util.*;
import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
#ManagedBean
#ViewScoped
public class Mfa02BackingBean implements Serializable
{
#ManagedProperty(value = "#{viewController}")
private ViewController viewController;
private Mfa02FormVO mfa02FormVO;
#PostConstruct
public void init()
{
mfa02FormVO = (Mfa02FormVO) JSFUtils.getObjectFromRequest(Mfa02FormVO.class.getName());
}
public String doMFA02()
{
viewController.setDestinationPage("mfa01");
return "viewController";
}
public void validateData ( FacesContext facesContext, UIComponent uiComponentToValidate, Object value)
{
String message = "";
String requestData = (String) value;
if ( !requestData.contains( "0" ) )
{
((UIInput )uiComponentToValidate).setValid( false );
message = "Incorrect format for Unit ID";
facesContext.addMessage( uiComponentToValidate.getClientId(facesContext), new FacesMessage(message) );
//JSFUtils.addObjectToRequest(Mfa02FormVO.class.getName(), mfa02FormVO);
}
}
.....getters&setters
}
Mfa01FormVO & Mfa02FormVO are just value objects with getters & setters
The main issue I encountered was saving object data between requests. When I go from mfa01 to mfa02 I do a lookup populate some vos and then pass a form vo into the Flash. Mfa02 screen is then constructed with the vo values. The big issue I had was when there was a validation error on mfa02. Initially I had the bean set to requestScoped however I lost the vo object after the validation failed. This was despite me adding the object back into the flash as soon as I got it from the Flash. The strange thing about this though was there was no guarantee when I would lose the object. If I clicked again sometimes I would lose the object straight away but sometimes it would remain for 1 more click and then i'd lose it.
I then changed mfa02backingbean to ViewScoped. However because it's completely ajax mfa02backingbean was not getting reinitialised on subsequent requests. This meant that whatever I set the values to on the first request would then be displayed forever more. I finally resolved this using ViewScoped and changing the Command Button to use an Action rather than ActionListener and in that method I return "viewController" String. I assume the lifecycle thinks I'm returning to a new view so it clears out anything left in the viewscope.
I hope this code helps anyone else looking for a similar solution.

Wired component null in seam EntityHome action

I have a custom EntityHome class. I wire the dependent entity in the wire method, but when I call the action (persist) the wired component is always null.
What could be the reason, similar code generated by seam gen is apparently working.
Here is the entity class.
The round.teeSet component is always null when persist is called, although it is shown in the jsf page.
Note: Seam version: 2.2.0.GA Jboss:
jboss-5.1.0.GA
I have overrden persist method to log the value of the wired element.
#Name("roundHome")
#Scope(ScopeType.CONVERSATION)
public class RoundHome extends EntityHome<Round>{
#In(required = false)
private Golfer currentGolfer;
#In(create = true)
private TeeSetHome teeSetHome;
#Override
public String persist() {
logger.info("Persist called");
if (null != getInstance().getTeeSet() ) {
logger.info("teeSet not null in persist");
} else {
logger.info("teeSet null in persist");
// wire();
}
String retVal = super.persist(); //To change body of overridden methods use File | Settings | File Templates.
return retVal;
}
#Logger
private Log logger;
public void wire() {
logger.info("wire called");
TeeSet teeSet = teeSetHome.getDefinedInstance();
if (null != teeSet) {
getInstance().setTeeSet(teeSet);
logger.info("Successfully wired the teeSet instance with color: " + teeSet.getColor());
}
}
public boolean isWired() {
logger.info("is wired called");
if(null == getInstance().getTeeSet()) {
logger.info("wired teeSet instance is null, the button will be disabled !");
return false;
}
else {
logger.info("wired teeSet instance is NOT null, the button will be enabled !");
logger.info("teeSet color: "+getInstance().getTeeSet().getColor());
return true;
}
}
#RequestParameter
public void setRoundId(Long id) {
super.setId(id);
}
#Override
protected Round createInstance() {
Round round = super.createInstance();
round.setGolfer(currentGolfer);
round.setDate(new java.sql.Date(System.currentTimeMillis()));
return round;
}
}
Here the 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:s="http://jboss.com/products/seam/taglib"
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:a="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
template="layout/template.xhtml">
<ui:define name="body">
<h:form id="roundform">
<rich:panel>
<f:facet name="header>">
#{roundHome.managed ? 'Edit' : 'Add' } Round
</f:facet>
<s:decorate id="dateField" template="layout/edit.xhtml">
<ui:define name="label">Date:</ui:define>
<rich:calendar id="date" datePattern="dd/MM/yyyy" value="#{round.date}"/>
</s:decorate>
<s:decorate id="notesField" template="layout/edit.xhtml">
<ui:define name="label">Notes:</ui:define>
<h:inputTextarea id="notes" cols="80" rows="3" value="#{round.notes}" />
</s:decorate>
<s:decorate id="totalScoreField" template="layout/edit.xhtml">
<ui:define name="label">Total Score:</ui:define>
<h:inputText id="totalScore" value="#{round.totalScore}" />
</s:decorate>
<s:decorate id="weatherField" template="layout/edit.xhtml">
<ui:define name="label">Weather:</ui:define>
<h:selectOneMenu id="weather" value="#{round.weather}">
<s:selectItems var="_weather" value="#{weatherCategories}" label="#{_weather.label}"
noSelectionLabel=" Select " />
<s:convertEnum/>
</h:selectOneMenu>
</s:decorate>
<div style="clear: both;">
<span class="required">*</span> required fields
</div>
</rich:panel>
<div class="actionButtons">
<h:commandButton id="save" value="Save"
action="#{roundHome.persist}"
rendered="#{!roundHome.managed}" />
<!-- disabled="#{!roundHome.wired}" /> -->
<h:commandButton id="update" value="Update" action="#{roundHome.update}"
rendered="#{roundHome.managed}" />
<h:commandButton id="delete" value="Delete" action="#{roundHome.remove}"
rendered="#{roundHome.managed}" />
<s:button id="discard" value="Discard changes" propagation="end"
view="/Round.xhtml" rendered="#{roundHome.managed}" />
<s:button id="cancel" value="Cancel" propagation="end"
view="/#{empty roundFrom ? 'RoundList' : roundFrom}.xhtml"
rendered="#{!roundHome.managed}" />
</div>
<rich:tabPanel>
<rich:tab label="Tee Set">
<div class="association">
<h:outputText value="Tee set not selected" rendered="#{round.teeSet == null}" />
<rich:dataTable var="_teeSet" value="#{round.teeSet}" rendered="#{round.teeSet != null}">
<h:column>
<f:facet name="header">Course</f:facet>#{_teeSet.course.name}
</h:column>
<h:column>
<f:facet name="header">Color</f:facet>#{_teeSet.color}
</h:column>
<h:column>
<f:facet name="header">Position</f:facet>#{_teeSet.pos}
</h:column>
</rich:dataTable>
</div>
</rich:tab>
</rich:tabPanel>
</h:form>
</ui:define>
</ui:composition>
I suppose You have a single ScopeType.CONVERSATION (RoundHome) instance without any long-running conversation enabled. So you may have a Temporary conversation which survive until The render response phase.
When you submit your form, because you have a Temporary conversation, You just populate what you submit. It explains why your TeeSet is null. (Your submitted form does not contain any reference to a Tee instance)
If you really want your TeeSet survive until The next submit, you must enable a long-running conversation before showing and disable after submitting your form.
Just an advice: Each Home instance is by default ScopeType.CONVERSATION - inherited from Home. So you can set up your RoundHome as follows
#Name("roundHome")
public class RoundHome extends EntityHome<Round> {
...
}

Categories