I'm getting deeper into JSF 2.0 at the moment and lacking a bit of understanding about the "transport" of managed bean properties from one view to the other. I searched a bit but haven't found a really good example, so if anyone could point me to a tutorial or explain the things a little bit I'd really grateful.
So here is my scenario:
I'm developing a small playground calendar application. The first view select.xhtml contains the calendar selector, where the user can pick a specific date:
<html>
...
<h:form>
<!-- Calendar selector from primefaces -->
<p:calendar value="#{calendarSelect.date}" mode="inline" navigator="true" />
<p:commandButton value="Show entries for date" action="day" />
...
My corresponding backing bean looks like this:
#ManagedBean(name="calendarSelect")
#RequestScoped
public class CalendarSelectComponent {
private Date date = null;
... // Getters and setters
Now when I submit the form from select.xhtml I'm forwarded to day.xhtml
<html>
...
<h:form>
The current day ist:
<h:outputText value="#{calendarEdit.date}">
<f:convertDateTime pattern="dd.MM.yyyy" />
</h:outputText>
The backing bean now looks like this:
#ManagedBean(name="calendarEdit")
#ViewScoped
public class CalendarEditComponent implements Serializable {
private Date date = null;
private CalendarEntryBean currentEntry = null;
private List<CalendarEntryBean> allEntries = null;
....
I am now trying to solve the problem: How do I transfer the date parameter from the selector to the editor?
I've tried a number of options, one was this:
<p:commandButton value="Show entries for date" action="day" />
<f:setPropertyActionListener target="#{calendarEdit.date}" value="#{calendarSelect.date}" />
</p:commandButton>
A debugger shows, that indeed, the date property of the calendarEdit is populated with the value from calendarSelect, but since day.xhtml is a new view, a new CalendarEditComponent backing bean is being created and not the one I've populated with the date from the selector in the select view.
I've read that one solution would be to create a SessionScoped backing bean that does retain all it's values. But this is not the way I think it's supposed to work, because I don't really need the information in the session, I simply want it to "travel" from A to B. Another downside with the session based approach is that I can only use one selector and one editor per session - which I think isn't acceptible if you think of multi window browsing and so on.
I really don't think I'm the first one encountering such a scenario and I'm sure that JSF provides an elegant solution for this but I haven't been able to find that solution.
So once again, if anyone knows how to approach this - I'm listening! ;-)
The <f:setPropertyActionListener> is executed during invoke action phase of the form submit. So it expects that the value is still there at that point. But since your select bean is request scoped, it isn't there during form submit anymore. You want instead to pass a request parameter which get inlined in the output during render response. You can do this with <f:param>.
<p:commandButton value="Show entries for date" action="day" />
<f:param name="date" value="#{calendarSelect.dateAsString}" />
</p:commandButton>
It'll be available as request parameter (note that it only understands Strings, due to the nature of HTTP). You could let JSF set request parameters as managed properties, but since your edit bean is view scoped, this isn't possible with #ManagedProperty. You've got to gather it yourself by ExternalContext.
String dateAsString = externalContext.getRequestParameterMap().get("date");
True, that's clumsy. I would just have used the same bean and view for this and toggle visibility of select/edit forms by rendered attribute. The edit view is after all not directly openable/bookmarkable by a simple GET, isn't it? ;)
Related
I have the next Java model bean (which contains not only a date in milliseconds but also a time zone)
public class Device {
private Calendar lentDate;
// getters and setters
}
and the next xhtml page fragment
<rich:dataTable value="#{tagBean.devices}" var="device">
<rich:column>
<h:outputText value="#{device.lentDate.time}">
<f:convertDateTime pattern="dd.MM.yyyy" timeZone="#{device.lentDate.timeZone}"/>
</h:outputText>
</rich:column>
</rich:dataTable>
But the timeZone attribute does not get the device.lentDate.timeZone value. Looks like it is because when the f:convertDateTime tag is rendered, the device variable is not available yet.
Is it possible to force JSF to render the f:convertDateTime tag after the device variable is available? Or the only way to make the timeZone set properly for each device in this case is to create a custom date/time converter?
Thank you.
The problem is that the attributes of the converter are evaluated when the view is build. This is partially because a converter is not a component itself, but in Facelets implemented by a TagHandler that sets a converter instance in the parent component. See JSTL in JSF2 Facelets... makes sense? for a very thorough background explanation.
You can solve this by using the <o:converter> from OmniFaces that was specifically build for this use case. Contrary to the regular converters, this one does evaluate its attributes when its parent component is being rendered.
See: http://showcase.omnifaces.org/taghandlers/converter
Sorry, really really basic question...
I've got a list of 'messageboard threads' that I display on a page. I want to be able to click on one of them and redirect to a page which displays the details for that thread. I'm struggling despite googling it for a while...
(I'm using PrimeFaces by the way)
Here's the relevant code from my 'list' page:
<p:commandLink value="#{thread.title}" action="#{messageboardBean.showThread()}">
<f:param name="threadId" value="#{thread.id}" />
</p:commandLink>
(it's in an h:form element)
This is part of my named bean (other methods work fine)
...
#ManagedProperty(value="#{param.threadId}")
private Long threadId;
...
public String showThread() {
System.out.println("id is " + getThreadId());
return "messageboard/list";
}
...
As you can see my method isn't implemented yet, but it's not even being called. Please can someone tell me why?
I tried with an h:link too by the way, same (or similar) problem.
Thanks!
UPDATE - Solved
Thanks to the help below, here is my solution (I've renamed 'messageboard' to 'discussion').
The link is generated using this code
value: what to display on the page, in my case the title of my discussion
outcome: refers to edit.xhtml, the faces file I want to go to
...and the [request] param is going to be called 'threadId' and has a value of the id attribute in my 'thread' object.
In the edit.xhtml page, I've got this
<f:metadata>
<f:viewParam name="threadId" value="#{viewDiscussionBean.threadId}" />
<f:event type="preRenderView" listener="#{viewDiscussionBean.loadDiscussion}" />
</f:metadata>
Note that 'threadId' is the same as the param name in the first page, and it is bound to my viewDiscussionBean's threadId property.
Then once the params are all set on my bean, I call the loadDiscussion method of my viewDiscussionBean. Since it now has the threadId property, it's able to do this (see below)
My viewDiscussionBean has the following managed property (I think the viewParam tag sets this, rather than the other way around).
#ManagedProperty(value="#{param.threadId}")
private Long threadId;
private Discussion thread;
So once that's set, this method is able to run (because it now has an ID)
public String loadDiscussion() {
thread = mbDao.find(threadId);
return "edit";
}
This just uses a DAO (using Hibernate) to look up the discussion thread with that ID, and set it in the bean. And then...
In my edit.xhtml page, I can access values in the discussion thread using things like
<h:outputText value="#{viewDiscussionBean.thread.message}" />
Voila! Thanks.
There are many possible caused for an UICommand action not being invoked. You can find them all here: commandButton/commandLink/ajax action/listener method not invoked or input value not updated Among others a missing form, a nested form, or a conversion/validation error elsewhere in the form, being in a datatable whose datamodel is not properly preserved, etcetera.
As to your particular functional requirement, better is to use a <h:link>. A command link issues a POST request which does in your particular case not end up with a bookmarkable URL with the thread ID in the URL. A <h:link> creates a fullworthy link which is bookmarkable and searchbot-indexable.
E.g.
<h:link value="#{thread.title}" outcome="messageboard/list">
<f:param name="threadId" value="#{thread.id}" />
</h:link>
This doesn't require a form nor an action method. You only need to move that #ManagedProperty to the bean associated with messageboard/list, or maybe replace it by <f:viewParam> in the messageboard/list view which allows for finer-grained validation/conversion.
See also:
Communication in JSF2 - Processing GET request parameters
When should I use h:outputLink instead of h:commandLink?
ViewParam vs #ManagedProperty(value = "#{param.id}")
Your best bet it probably to go with BalusC's answer and use <h:link>. However, I have some thoughts about the behavior you're seeing.
Primefaces <p:commandLink> (like <p:commandButton>) uses ajax by default. Therefore, there won't be any actual navigation resulting from returning an outcome from your action. This could make it look like your action isn't being invoked. Try adding ajax=false to your <p:commandLink>.
When using <h:link>, navigation is resolved when the link is rendered rather than when it's clicked. Modifying your example:
<h:link value="#{thread.title}" outcome="#{messageboardBean.showThread()}">
<f:param name="threadId" value="#{thread.id}" />
</h:link>
showThread() will be called (I think) when the view containing the link is being rendered. If you're not checking at the right time, this may also make it look like the method is being called.
In both cases, this doesn't explain why you wouldn't see the message to System.out at all, but I know I've tried that thinking it was fail-safe and still not seen the output, which all depends on your container, configuration, how you launched it, etc.
I have a little issue regarding submit of forms in jsf 2:
In my webapp I got a function that updates entities in my db. This function gets the relative data out of a managed bean.
So, my issue is that changes in my view are not passed to the managedBean. To make that clear for you, here an example:
public String updateProject() {
projectService.updateProject(projectData.getProjectDTO());
return ("overview.xhtml");
}
prjectData is my ManagedBean. This one doesn't work! No updates are been made.
public String deleteProject() {
projectData.getProjectDTO().setDeleted(true);
projectService.updateProject(projectData.getProjectDTO());
return ("overview.xhtml");
}
Here, when I change a value by code it works! So I guess my values out of the view are not passed to my managedBean.
Where could be a mistake? Is there maybe an action I have to invoke to make the data pass my view to the managedBean?
Answer to Gabor's comment:
My page looks like:
<h:form>
<h:commandLink action="#{controller.updateProject}" value="Edit" />
<h:outputLabel for="title" value="Titel" />
<h:inputText id="title" value="#{projectData.projectDTO.title}" />
</h:form>
If I change the title here and press update nothing happens ;-)
My Controller looks like:
#ManagedBean
#RequestScoped
public class Controller {
#ManagedProperty(value = "#{projectData}")
private ProjectData projectData;
...
For unknown reason my debug mode in eclipse doesn't work anymore it ignores my breakpoints all the time. I gonna fix that and then I'll check about the instances. Sry -.-
Either projectData or projectDTO is not the right instance as you expect it to be. It's a completely different instance. Aren't you eagerly recreating/overriding beans? Don't you have multiple beans in the scope? Shouldn't it for example be #{controller.projectData.projectDTO.title}? Shouldn't the projectData instance in the controller be a managed property?
Is your projectData bean also RequestScoped? Try to change to ViewScoped. RequestScoped beans are recreated for each request, also ajax request. And what is scope of projectService?
I am trying to creating a small wizard using jsf1.2 sun RI +richfaces.
The user sets input data in multiple pages and in the end presses finish upon which the data is inserted in database.
The info is displayed as a row in a table with edit button.
when edit is clicked the data is populated in all the pages and user can edit and save it again.
Also while navigating back and forth the data must be persisted in the forms.
Currently i have implemented this by using multiple session scope beans that access each other's data by looking its reference in session map and some are injected as dependencies.
i would like to know if there's a standard way / good pattern of doing this by avoiding multiple session scope beans and doing lookups in session map?
if someone could throw a link to any blog/tutorial with similar example that would be great
many thanks
You can just make them properties of a single session scoped bean.
public class Wizard {
private Step1 step1 = new Step1();
private Step2 step2 = new Step2();
private Step3 step3 = new Step3();
// ...
}
and use it as follows
<h:form id="step1">
<h:inputText id="input1" value="#{wizard.step1.input1}" />
<h:inputText id="input2" value="#{wizard.step1.input2}" />
</h:form>
...
<h:form id="step2">
<h:inputText id="input1" value="#{wizard.step2.input1}" />
<h:inputText id="input2" value="#{wizard.step2.input2}" />
</h:form>
...
This way you just have instant access to everything from inside the action methods.
I would recommend to you to download an open source with components for JSF like PrimeFaces. It will make it much easier for you. They also have a wizard: PrimeFaces Wizard
I'm somewhat confused about the lifecycle of ManagedBeans of type "request".
In this example i'm using one request bean "userBean" to fill this page and one request bean "userEditBean" to fill the following edit page.
<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Name" />
<h:outputText value="#{userBean.user.name}" />
...
</h:panelGrid>
<h:commandButton value="Edit" action="edit" actionListener="#{userEditBean.init}">
<f:attribute name="user" value="#{userBean.user}"/>
</h:commandButton>
</h:form>
When i press the Edit button a userEditBean is created but the attribute map resolves "user" to null.
Does this mean that the attribute EL is resolved after the userBean has already been destroyed? How can i pass values from incoming beans to outgoing beans?
You're setting the attribute value with an expression, not a static value. Whenever you request the value, the expression will be re-evaluated again. The userBean.user apparently isn't present in the subsequent request. You need to ensure that it is there (in other words, the constructor of the userBean should ensure that the user is been created and set.
There are however alternatives. One of the best is to use Tomahawk's <t:saveState> for that. Add it somewhere in the page:
<t:saveState value="#{userBean.user}" />
That said, I agree with Bozho that the whole approach is a bit strange, but that's another story. You may however get lot of useful ideas out either of the following articles: Communication in JSF and/or Using Datatables. Good luck.
request scope means the bean lives during one request. And you fill your edit page (1st request), and send the edited user (2nd request).
In addition to that, <f:attribute> sets tha attributes in the parent component, not in the request. So in your code the attributes will be found in the button.getAttributes() (if you have bound your button).
Furthermore, it is strange to have an actionListener method named init. Since you don't need the event, you can set the action to be the method which will do the editing operation, and make that method return the navigation-rule you want.