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.
Related
I have a home page that is written in JSF 2.2. It uses a public template that is shared by many other pages. I don't want to include <f:view transient="true"> in the public template because I want to make only this particular home page as transient.
Now I have this code in my home page
<f:view transient="true">
<ui:define name="content">
.....
<h:commandLink value="#{msg['homepage.createaccount']}"
action="#{homepageController.createNewAccount()}" />
</ui:define>
</f:view>
now HomepageController is a named session bean.
#Named
#Session
public class HomepageController {
//code here
}
The <f:view = "transient"> does not seem to work. Is it because the controller is Session scoped ?
The controller is session scoped because while loading the first page, we fetch several important lists that are used later by many parts of application.
In the User Guide, it is stated that RequestContext will work on both Ajax and non-Ajax calls. However, all of the examples in the User Guide are using Ajax and in my case, it doesn't work with non-Ajax calls.
The following is a test 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:p="http://primefaces.org/ui">
<h:head>
<title>Test page</title>
</h:head>
<h:body>
<h:form >
<p:commandButton ajax="false" value="Non-Ajax" actionListener="#{mrBean.show}" />
<p:commandButton value="Ajax" actionListener="#{mrBean.show}" />
</h:form>
<p:dialog modal="true" id="statusDialog" widgetVar="statusDlg" closable="false" >
<h:outputText value="Helllooo" />
</p:dialog>
</h:body>
</html>
And this is the managed bean:
#ManagedBean
#RequestScoped
public class MrBean {
public void show() {
System.out.println("SHOW DIALOG");
RequestContext context = RequestContext.getCurrentInstance();
context.execute("statusDlg.show();");
}
}
If I click the Ajax button, the dialog is shown correctly. However, the Non-Ajax button did nothing. In both case, the SHOW DIALOG message was printed on the console.
I'd be very grateful if you could show me how to tackle this problem :).
Best regards,
James Tran
I must disappoint you, but RequestContext in non AJAX request is available but only useful function is isAjaxRequest() with boolean return value. In documentation and API reference you can see that for all other method it is told that they are for AJAX requests. Also for execute() method:
Execute a javascript after current ajax request is completed.
At past, RequestContext wasn't available at all, now it is just available for possible future use with non AJAX requests. Additionally see this issue
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..
I'm trying for the first time to use templates with JSF 2.0 with Eclipse, but I'm having problems.
The original index.xhtml page works correctly, and when I click on a button, everything works fine. However, if I change the index page so that it uses a template file it no longer works properly. The modified index.xhtml page is here:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
template="/templates/main-template.xhtml">
<ui:define name="title">
Simulator using JSF 2.0 - Test Version 2
</ui:define>
<ui:define name="header">
Home Page of the Simulator using JSF 2.0 - Test Version 2
</ui:define>
<ui:define name="body">
Click on the button to select the required option
<h:outputText value="and login" rendered="#{!login.loggedIn}"/>
<h:form prependId="false">
<h:commandButton value="Option 1" action="#{login.option1}"/>
<h:commandButton value="Option 2" action="#{login.option2}"/>
<h:commandButton value="Option 3" action="#{login.option3}"/>
<h:commandButton value="Logout" disabled="#{!login.loggedIn}" action="#{login.logout}"/>
</h:form>
</ui:define>
</ui:composition>
and the template file, main.template.xhtml, is in the sub-folder templates, is here:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>
<ui:insert name="title">Title</ui:insert>
</title>
</h:head>
<h:body>
<ui:insert name="header">Header</ui:insert>
<br/>
We are in template.xhtml
<br/>
<ui:insert name="body">Body</ui:insert>
</h:body>
</html>
If I remove all code with the "h" tags in index.xhtml, the file picks up correctly the code in templates/main-template.xhtml, so the path is correct. However, if I include code with the "h" tags, as is here, Eclipse complains that the tags are not recognized and the page fails.
If I include the line xmlns:h="http://java.sun.com/jsf/html" at the top, then Eclipse recognized the "h" tags and the page is correctly rendered, but the application fails when I click a button, and returns the error:
javax.servlet.ServletException: javax.el.PropertyNotFoundException:
/index.xhtml #15,68 action="#{login.option1}": Target Unreachable,
identifier 'login' resolved to null
Perhaps somehow the line xmlns:h="http://java.sun.com/jsf/html" in the template file is messing things up, but the whole idea of templates is to include as much common code in a template file.
Does anybody have any idea what is going on, and what the solution to this is?
The web.xml and faces-config.xml are standard, and don't think anything has to be done with them.
Your idea of how templates should work seems correct, but there are some points that we should clarify. Maybe this would help you:
Namespaces
About namespaces, whenever you use a tag library in a page, you should declare it's namespace. Even if you're using templates and you've declared them in your template. You could think that namespace declarations are not inherited, if it helps.
In this case I see that you index.xhtml page is using h:commandButton but hasn't declared its namespace.
Beans
For a bean to be found by EL you should have the following:
A class annotated with #ManagedBean importing from javax.faces.bean.ManagedBean package, like this:
import javax.faces.bean.ManagedBean;
#ManagedBean
#ViewScoped
public class Login
{
// ...
}
In this case your bean should be found by EL by the name login, by convention. (Decapitalize the first letter of your class name)
Or you could give it a name:
import javax.faces.bean.ManagedBean;
#ManagedBean(name="login")
#ViewScoped
public class MyLoginBean
{
// ...
}
In this case, by convention it would be called myLoginBean but we gave it a name, in this case login, so EL should find it by the name login.
If you want to use CDI instead of plain JSF, you could use #Named annotation to define how your bean should be found by EL, following the same naming convention.
import javax.inject.Named;
import javax.enterprise.context.RequestScoped;
#Named(value="login")
#RequestScoped
public class MyLoginBean
{
// ...
}
Remember that data that you want EL to find and change must have the proper getters and setters.
Hint
I would kindly suggest that you create simpler code in order to test funcionality. In this case you could test templating first and then bean, actions, etc...
I hope it helps.
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.