pass params between JSF beans for a single view.xhtml - java

Possibly I'm using forms incorrectly. The idea is that detail.content displays some HTML, and this portion works fine. The form is supposed to allow multiple (one-to-many notes to be entered and displayed on the right.
While the default note is displayed, more notes are not. How do I link the note bean to the detail bean? I was thinking of a String "id" and passing that from one bean to another.
This is similar to the idea of passing params from one view.xhtml to another, except that it's all on one page. I would like to keep the beans distinct. Ultimately, I would like to do this with EJB's, and so want to keep that option open, while not using EJB's yet.
view, detail.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:ui="http://java.sun.com/jsf/facelets"
template="./complexTemplate.xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:f="http://java.sun.com/jsf/core">
<ui:define name="top">
<h:form>
<h:outputLink id="back" value="detail.xhtml">
<f:metadata>
<f:viewParam name="id" value="#{detail.id}" />
</f:metadata>
<f:param name="id" value="#{detail.back()}" />
<h:outputText value="back" />
</h:outputLink>
</h:form>
<h:form>
<h:outputLink id="forward" value="detail.xhtml">
<f:metadata>
<f:viewParam name="id" value="#{detail.id}" />
</f:metadata>
<f:param name="id" value="#{detail.forward()}" />
<h:outputText value="forward" />
</h:outputLink>
</h:form>
</ui:define>
<ui:define name="content">
<h:outputText escape="false" value="#{detail.content}"></h:outputText>
</ui:define>
<ui:define name="right">
<p>#{notes.note.id}</p>
<p>#{notes.note.comment}</p>
<h:form>
<h:inputText value="#{notes.note.comment}" />
<h:commandButton value="add note"
action="#{notes.commentAction()}"/>
</h:form>
</ui:define>
</ui:composition>
bean, Notes.java:
package net.bounceme.dur.nntp;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.context.Dependent;
import javax.inject.Named;
#Named
#Dependent
public class Notes {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(Notes.class.getName());
private static final Level level = Level.INFO;
private Note note = new Note();
public Notes() {
logger.log(level, "Notes..");
}
public Note getNote() {
return note;
}
private void setNote(Note note) {
this.note = note;
}
public void commentAction() {
logger.log(level, "Notes.newNote.."); note.setId("messageIdGoesHere");
note.setComment("hmmm");
}
}
The other bean, Details, works fine. However, I'm not sure how to integrate two beans onto one page so that the two beans are aware of each other.

Use #Inject.
#Named
public class Notes {
#Inject
private Detail detail;
}
It'll be available in the Notes instance during the lifetime beyond the #PostConstruct method. The other way round can also. It's not entirely clear what the parent-child relationship is in your particular case.
Unrelated to the concrete problem, you've there some odd view markup. Only one <f:metadata> in top of definition is sufficient. Also, #{detail.back()} as a value expression is odd. You should have a getBack() method and reference it as #{detail.back}. The same for forward(). Also, EJBs have nothing to do with this all. Whether you plan to use EJBs or not is irrelevant to this particular issue. I'd suggest learning JSF by a decent book/tutorial, not by cobbling some loose snippets together while not understanding the complete picture.

Related

How does the POST REDIRECT GET works in JSF on commandButton

I was testing couple of new features of JSF and I came across Post Redirect Get.
I wanted to redirect from my first page say first.xhtml to second.xhtml.
I have a number as a property in both the managed beans and I wanted to pass it to the second bean from the first bean using request parameter.
This is my first page
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<head>
<title>Landing Page</title>
</head>
<body>
<h3>Enter Number</h3>
<h:form>
<h:inputText id="input" name="number" value="#{postRedirectGet.number}" />
<h:commandButton value="redirect to result"
action="resultPage?faces-redirect=true&includeViewParams=true">
</h:commandButton>
</h:form>
</body>
</html>
And in the second page I have
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:metadata>
<f:viewParam name="number" value="#{postRedirectResult.number}"/>
</f:metadata>
<head>
<title>Result Page</title>
</head>
<body>
<h:form>
<h:outputText value="Number #{postRedirectGet.number}" />
<h:outputText value="Number #{postRedirectResult.number}" />
<h:commandButton value="Redirect to index" action="/index?faces-redirect=true" />
</h:form>
</body>
</html>
Now the page is doing a POST using commandButton and then redirecting to second page from first but it passes number=0 in the URL. It works if I change
<f:viewParam name="number" value="#{postRedirectResult.number}"/>
to
<f:viewParam name="number" value="#{postRedirectGet.number}"/>
but I thought the viewParam is used to set the value to a bean and not used to pass the values in URL. Could someone please explain how can we do POST and set the property of the managed bean on next page.
The problem is that the f:viewParam is used in two different ways in two scenarios . In h:link it is used to set the property of target bean , in h:commandButton it is used to compute the GET URL and then the target bean property can be set using #ManagedProperty . Is my understanding correct or can the f:viewParam be used to set the property in h:commandButton POST redirect get also.
What you seem to be missing is what includeViewParams does. Let me quote this very informative article (you should read all of it):
The other special query string parameter, includeViewParams, tells the navigation handler to include the view parameters when performing the navigation. But what view parameters should be included? The view parameters to be included when performing the navigation are declared on the to-view-id page.
So JSF looks at your resultpage.xhtml to determine which parameters to pass. And then dutifully proceeds to pass the current value of postRedirectResult#number (which at this time is unset/0).
To have the GET number parameter reflected in your bean, pass it as a real parameter:
<h:commandButton value="redirect to result"
action="resultPage?faces-redirect=true&number=4" />
See also:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
There are different 4 ways to transfer data from JSF Page To Backing Bean.
We can use
f:param
f:setPropertyActionListener
f:attribute
Method expression (JSF 2.0).
Here you can try f:setPropertyActionListener as..
<h:commandButton value="redirect to result"
action="resultPage?faces-redirect=true">
<f:setPropertyActionListener target="#{postRedirectResult.number}" value=4 />
</h:commandButton>
Here is the link for this.

JSF2 commandButton; AJAX or non AJAX, it refreshed the whole page

this is a simple example which demonstrates the case.
you have a form with a Panel and two commandButton, one is AJAX the other is not. by clicking on any of them, an InputText will be created in the backing bean and added to the Panel.
My managed bean:
#ManagedBean
public class DynamicPanel {
private Panel dynmaic;
public Panel getDynmaic() {
return dynmaic;
}
public void setDynmaic(Panel dynmaic) {
this.dynmaic = dynmaic;
}
public String adddynamic(){
InputText text = new InputText();
dynmaic.getChildren().add(text);
text.setValue(text.getId()+" Size= "+ dynmaic.getChildren().size());
return null;
}
public String removeall(){
this.dynmaic.getChildren().clear();
return null;
}
}
My XHTML page
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui" xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form>
<p:panel id="dynamic" binding="#{dynamicPanel.dynmaic}">
</p:panel>
<h:commandButton value="Add with AJAX" id="ajaxBtn" >
<f:ajax onevent="onClick" execute="#{dynamicPanel.adddynamic()}" render="dynamic" />
</h:commandButton>
<h:commandButton value="Add" action="#{dynamicPanel.adddynamic}" />
<h:commandButton value="remove all" action="#{dynamicPanel.removeall}" />
</h:form>
</h:body>
</html>
my faces-config.xml is empty.
Now, I have three issues with the code above. Could someone please clarify it to me, I'm new to JSF2.
the first is, why both command buttons behave the same? clicking on ether one would refresh the whole page.
the second issue is, why clicking on the non AJAX commandButton adds two Inputfieds at a time?
the third is, why changing the scope of the managed bean to #SessionScoped will give an error once you load the page? ( somehow just loading the page, the form issues an ajax request without me clicking on the commandButton. Why is that?
Try the following, that should work better.
<h:commandButton value="Add with AJAX" id="ajaxBtn" >
<f:ajax onevent="click" execute="ajaxBtn"
render="dynamic"
listener="#{dynamicPanel.adddynamic()}"
</h:commandButton>
As far as I know, for f:ajax execute attribute the id of the components should be given, and you should call methods such as addDynamic() in listener attribute.
When you click on the non-ajax button, it posts the whole form which also includes your ajax command. so basically your addDynamic() function is called twice, one through ajax command and the second through non-ajax command..

Conversation not propagated automatically on form submission?

I have the following conversation scoped backing bean:
#Named
#ConversationScoped
public class TestConversation implements Serializable {
private Logger logger = LoggerFactory.getLogger(TestConversation.class);
private List<Integer> numbers;
#Inject
private Conversation conversation;
#PostConstruct
public void init() {
logger.info("Creating TestConversation bean!!!");
numbers = new ArrayList<Integer>();
numbers.add(3);
numbers.add(4);
numbers.add(5);
numbers.add(6);
conversation.begin();
}
public void commandLinkAction() {
logger.info("Invoking commandLinkAction");
}
public List<Integer> getNumbers() {
return numbers;
}
}
And the following facelets view:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Testing Conversation</title>
</h:head>
<h:body>
<h:form>
<h:dataTable value="#{testConversation.numbers}" var="num">
<h:column>
<h:outputText value="#{num}"/>
</h:column>
<h:column>
<h:commandLink action="#{testConversation.commandLinkAction}">Trigger form submission</h:commandLink>
</h:column>
</h:dataTable>
</h:form>
</h:body>
</html>
When I enter the page I see INFO: Creating TestConversation bean!!! which is correct.
But then I click on the h:commandLink and I see:
INFO: Creating TestConversation bean!!!
INFO: Invoking commandLinkAction
The bean was created again, which means that the conversation was not propagated. I think this contradicts with the following:
Quote from docs:
The long-running conversation context associated with a request that renders a JSF view is automatically propagated to any faces request (JSF form submission) that originates from that rendered page.
If I add this <f:param name="cid" value="#{javax.enterprise.context.conversation.id}"/> then everything works fine. Do I have a misunderstanding?
P.S Without the f:param it works fine when I click on the commandLink for the second time, but not on the first time:(.
I guess, the problem seems like conversation was not started when the <h:form> component was processed so the form's action did not had cid in it for the first time..
For the second time, when you click on a link, testConversation.commandLinkAction, access to testConversation made the conversation to start before processing the <h:form>
try the below change
If you place #{testConversation} before <h:form>
example works fine as converation is started before processing the <h:form>
Hope this helps..
Building on the previous answer, it's definitely because the TestConversation bean is not being constructed until it's already too late for the form to include the cid automatically.
In this case, you're initializing data for the view, so it's probably better to put it in a preRenderView event listener instead.
<f:event type="preRenderView" listener="#{testConversation.init}"/>
Put this early in your facelet template, such as in the f:metadata (as it's often used in conjunction with f:viewParam), and remove the #PostConstruct annotation. That makes the invocation of init explicit rather than relying on it being run as a side effect of the bean being constructed because it was referenced in an EL expression.

Passing Parameters With Seam, RichFaces & PopupPanel

I'm trying to get a little application working using Seam 3, RichFaces 4 and hitting a few troubles with passing some parameters around. I've tried a lot of different things but I keep falling at the final hurdle. I'm carrying through a customerId via a request parameter. But when I hit a RichFaces commandButton on a popupPanel, that customerId is no longer available to me.
I'm setting up an application to manage some data. Basically you select a customer from one screen, that will take you to another screen containing "repositories" where you then can create, edit etc. You can get to this second repositories page via the URL:
http://localhost:8080/media-manager/repositories.xhtml?customer=12
I then have a bean picking this value up:
#Named
#RequestScoped
public class RepositoryBean extends AbstractViewBean<Repository> {
// Various properties etc. here
private Long customerId;
public void init() {
log.info("Customer ID is "+ customerId);
}
}
I then set the customer ID via metadata on the repository page and call init:
<f:metadata>
<f:viewParam name="customer" value="#{repositoryBean.customerId}"/>
<f:event type="preRenderView" listener="#{repositoryBean.init}" />
</f:metadata>
This works well at the start. I can display information I need for the customer with the supplied ID. But when I try to create my popupPanel is goes a bit wrong. Here's a simplified version of the code first:
<rich:popupPanel id="repositoryModalPanel" modal="true" resizeable="true">
<f:facet name="header">Title</f:facet>
<f:facet name="controls">
<h:outputLink value="#" onclick="#{rich:component('repositoryModalPanel')}.hide(); return false;">X</h:outputLink>
</f:facet>
<h:form id="modalForm" class="modalForm">
<fieldset>
<ul class="layout form">
<li>
<label for="name" class="required">Name:</label>
<h:inputText id="name" value="#{repositoryBean.instance.name}" required="true">
<!-- rich:validator event="blur" / -->
</h:inputText>
<rich:message for="name" errorClass="error errormessage" />
</li>
<li class="last">
<a4j:commandButton id="create" value="Create" action="#{repositoryBean.saveRepository}" rendered="#{empty repositoryBean.instance.id}"/>
<a4j:commandButton id="save" value="Save" action="#{repositoryBean.saveRepository}" rendered="#{not empty repositoryBean.instance.id}"/>
<a4j:commandButton id="cancel" value="Cancel" action="#{repositoryBean.clearInstance}" immediate="true" />
</li>
</ul>
</fieldset>
</h:form>
Basically, whenever I hit the save commandButton, the init method is called but the customerId member is never populated. Does anyone have any insight into why?
I've read that viewParam is only for GET requests so maybe that's the problem? But if that's the case - what is the other solution? Lots of things I have seen suggested (e.g. using #ManagedProperty) does not seem to be applicable to Seam 3.
RepositoryBean is RequestScoped, and as such will be newly created on each request, especially if you hit the save button.
The straightforward solution is to promote RepositoryBean to be ConversationScoped, and to make it long running if you enter the page.
#Named
#ConversationScoped
public class RepositoryBean extends AbstractViewBean<Repository> {
// Various properties etc. here
#In
Conversation conversation
private Long customerId;
public void init() {
log.info("Customer ID is "+ customerId);
conversation.begin();
}
}
The easieast way for that is to dump the preRenderView and use seam 3 view-action instead.
<f:metadata>
<f:viewParam name="customer" value="#{repositoryBean.customerId}"/>
<s:viewAction action="#{repositoryBean.init}" if="#{conversation.transient}"/>
</f:metadata>

Partial rendering JSF components

dwelling over how to partial render (divs), by including different source files (with panels and components). Depending on menu actions. If understood the JSF phases correctly, the View is rebuilt during the Render Response, the last phase. And if I have events and actions, they will be invoked during the Invoke Application phase, the phase before.
All I want to do is to set the including xhtml page for a specific menu command via ajax, before the View is re-rendered. But the ui:include always get invoked before the menu action. I've tried with richfaces 4 (a4j:param, rich:panel, etc) and standard JSF 2.0 (f:param, h:panelGroup) components, but the the ui:include always get invoked before the action.
What should I do to process the menu action (to set the including page), before the ui:include gets invoked?
PS. This must be the standard patter, instead of including static content. But I find very few examples on this on the net ?!
Menu.xhtml
<rich:toolbar itemSeparator="line">
...
<rich:dropDownMenu mode="ajax">
<f:facet name="label">
<h:panelGroup>
<h:outputText value="Menu 1" />
</h:panelGroup>
</f:facet>
<rich:menuItem id="newActivityMenu" action="#{navigationBean.menuAction}" render="content" label="New">
<a4j:param id="newActivityParam" name="includeContentPage" value="/user/Create.xhtml" />
</rich:menuItem>
...
NavigationBean.Java
#ManagedBean
#RequestScoped
public class NavigationBean {
public String menuAction() {
String param = JsfUtil.getRequestParameter("includeContentPage");
this.includedContentPage = param;
JsfUtil.log(this, "Including Content Page : " +param);
FacesContext.getCurrentInstance().renderResponse();
return "";
}
public String getIncludedContentPage() {
if(includedContentPage == null)
return "";
else if(!includedContentPage.endsWith(".xhtml"))
includedContentPage += ".xhtml";
JsfUtil.log(this, "Get Content Page : " +includedContentPage);
return includedContentPage;
}
layoutClient.xhtml
...
<ui:define name="top">
<ui:include src="/resources/viewComponents/menuTop.xhtml"/>
</ui:define>
<ui:define name="content">
<ui:include src="#{navigationBean.includedContentPage}"/>
</ui:define>
...
masterLayout.xhtml (added)
...
<h:body>
<div id="top" >
<ui:insert name="top">Top Default</ui:insert>
</div>
<div id="left">
<ui:insert name="left">Left Default</ui:insert>
</div>
<ui:insert name="content">Content Default</ui:insert>
</h:body>
..
You must have a template page as well since you are defining top and content in layoutClient.xhtml so I think you are trying to be too general with the layoutClient.xhtml page as it appears to be functioning as a template as well. Lets assume your template page is called template.xhtml. The standard pattern you eluded to is to make your template page something like this:
template.xhtml
...
<ui:insert name="top">
<ui:include src="/resources/viewComponents/menuTop.xhtml"/>
</ui:insert>
...
<ui:insert name="content" />
...
This means that all your pages contain the menu at the 'top' (by default, they can override this) and that they must specify their own content, which makes sense.
Now, instead of trying to make a page like layoutClient.xhtml that does tricky stuff to determine which content is inserted, create each page seperately like this:
page1.xhtml
<ui:composition template="template.xhtml">
...
<ui:define name="content">
<p>This is a page that defines some content and also includes my menu that it inherits from template.xhtml</p>
</ui:define>
...
</ui:composition>
page2.xhtml
<ui:composition template="template.xhtml">
...
<ui:define name="content">
<p>This is another page that defines some content and also includes my menu that it inherits from template.xhtml</p>
</ui:define>
...
</ui:composition>
Both of these pages inherit your menu and put the content in the appropriate place.
With that kind of configuration, all your menuAction() method needs to do is return a link to page1.xhtml or page2.xhtml. Also, you don't need any complex use of parameters or manual calls to renderResponse() or a4j:param tags!

Categories