Call of action method from iterated h:commandButton fails - java

I am trying to implement a function that adds a book to a shopping cart.
The Cart bean has the fields to hold the request parameters. It is session scoped.
<h:form id="addBookToCartForm">
<ui:repeat var="book" value="#{search.bookList}">
<h:commandButton value="#{messages.addToCart}"
action="#{cart.addBookToCart}">
<f:param name="bookId" value="#{book.bookId}" />
</h:commandButton>
</ui:repeat>
</h:form>
The request from the view index.jsf, the action method calls the view cart.jsf.
The problem is: The action method isn't called, not even the bean itself is constructed.
I had a look at solutions for similar problems but couldn't find a solution that pointed in the right direction. At first I thought #{search.bookList} could be a problem but I don't need the bookList for the next view.
Activating debugging via <ui:debug> reveals this:
java.lang.UnsupportedOperationException: Not supported.
at javax.faces.component.UIViewAction.getActionListener(UIViewAction.java:249)
This message refers to the metadata at the top of index.jsf:
<f:metadata>
<f:viewParam name="genreTypeId" value="#{search.genreTypeId}" />
<f:viewAction action="#{search.searchByGenreTypeId}" />
</f:metadata>
The search bean is where the bookList comes from.

Update message using ajax update. It will show some validation message.

As it turns out the problem wasn't the iteration at all but the return value of the action methods my application is using.
In order to keep the view instance the methods have to return either null or void.

Related

Make JSF 2.0 execute methods as they are assigned to params, not store reference to method?

Before I begin, my apologies if the wording of this question's title is confusing. I hope my explanation here will make it much clearer.
In a JSF template of my application, I want to include another template and pass a parameter to it that holds the results of an application method. In this parent template, I have:
<ui:repeat var="loopObject" value="#{ApplicationBean.objectList}">
<ui:include src="anotherTemplate.xhtml">
<ui:param name="firstParam"
value="#{ApplicationBean.initForOtherTemplate(loopObject)}" />
</ui:include>
</ui:repeat>
It turns out, though, that initForOtherTemplate is not executed at this point and firstParam contains a reference to that method, rather than its return value, as I expected.
Actually, while initForOtherTemplate does have a return value, anotherTemplate.xhtml doesn't need it. However,the method does set up some other objects in ApplicationBean that this new template will use. For example, it sets values for importantInfo and importantInfoToo, which the other template needs.
anotherTemplate.xhtml contains:
<ui:remove>
<!--
When a parameter contains a method call, the method isn't executed until
the parameter is referenced. So we reference the parameter here and ignore
the results. There must be a better way.
-->
</ui:remove>
<h:outputText value="#{firstParam}" style="display: none;" />
<h:outputText value="#{ApplicationBean.importantInfo}" />
<h:outputText value="#{ApplicationBean.importantInfoToo}" />
If this template didn't reference firstParam, then importantInfo and importantInfoToo wouldn't be set or have unpredictable values. This is very disappointing, because I expected initForOtherTemplate to be executed in the parent template, rather than here, which feels messy.
How can I get the assignment of the parameter to actually execute the method immediately rather than store a reference to it?
The <ui:repeat> is an UIComponent which runs during view render time. The <ui:include> is a TagHandler (like JSTL) which runs during view build time. So at the moment <ui:include> runs, the <ui:repeat> isn't running and thus the #{loopObject} isn't available in the EL scope at all.
Replacing <ui:repeat> by <c:forEach> should solve this particular problem.
<c:forEach var="loopObject" items="#{ApplicationBean.objectList}">
<ui:include src="anotherTemplate.xhtml">
<ui:param name="firstParam"
value="#{ApplicationBean.initForOtherTemplate(loopObject)}" />
</ui:include>
</c:forEach>
See also:
JSTL in JSF2 Facelets... makes sense? - Substitute "JSTL" with "ui:include".

How to call managed bean in h:outputLink when opening new page

I have a h:outputLink which opens new JSF page and sends h:param.
<h:outputLink id="lnkHidden" action="#{HistoryLinkController.linkValue("test")}" value="HistoryLink.xhtml" style="display:none">
<f:param name="id" value="#{item.aSessionID}" />
</h:outputLink>
I want when I click it to send a value to a second managed bean. I tried to implement it with action="#{HistoryLinkController.linkValue("test")}" but I get error. Is there any attribute that I can use for this purpose?
Try using a <h:commandLink> in the following way and it should work fine
<h:commandLink id="hLink" value="History" action="#{HistoryLinkController.linkValue}" >
<f:param name="sessID" value="#{item.aSessionID}" />
</h:commandLink>
and the bean "HistoryLinkController" should have a method like
public String linkValue(){
// get "sessID" from FacesContext
...
return "/HistoryLink.xhtml";
}
Replace h:outputLink into h:commandLink.
I have also tried to navigate to some view along with passing a value to another bean already but I didn't get it. But what I got is---
U can remain in the same bean class if possible, and use navigation rules in faces-config.xml for navigating to another page.
And In ajax, action will be called earlier than its actionListener.
Hope it helps you finding a way...

How to use both Navigation Rule and f:ajax

Here's my scenario: I'd like to update a page via Ajax in some cases, in other cases, execute a navigation rule. My use case is a login form. I'd like them to receive an error message via ajax if their uname/password fails, but navigate to a new page if it succeeds.
Has anyone done this using JSF2.0 f:ajax apis? I'm not really interested in solutions that go outside standard facelets, jsf2.0, etc.
It's not different from when doing it without ajax. Just return the next view ID as String the usual way via <h:commandXxx action> (and thus not <f:ajax listener>).
So, just
<h:commandButton value="Login" action="#{bean.login}">
<f:ajax execute="#form" render="#form" />
</h:commandButton>
with
public String login() {
// ...
return "nextpage";
}
will work as good as without <f:ajax>. It'll just go to nextpage.xhtml.
See also:
Differences between action and actionListener
JSF f:ajax listener vs commandButton action

JSF property transfer from backing bean A to backing bean B

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? ;)

Passing data from request ManagedBeans in JSF

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.

Categories

Resources