Choosing how to pass parameters to a target bean/page using JSF - jsf

I have been using JSF for a few years now, but I still have doubts when it comes to deciding how to pass parameters to a target page/bean.
I do think this question is a bit complex, and that some may tell me to break it down into smaller questions. But, I also think that the answer to all of the questions bellow are related, and that it addresses the lack of intuitiveness when all you want JSF to do is: "Go to that page and pass this as a parameter".
First, how to decide between Forward and Redirect?
After that, how to choose between h:commandLink/h:commandButton, h:link or h:outputLink?
Then, combined with the option I choose above, should I use f:param or f:setPropertyActionListener? Will both properly pass parameters to the target bean, independently of its scope?
Finally, on the target bean/page, when should I use f:viewParam, or recover parameters from the request programmatically?

I'm going to answer your questions based on my own experience. Some of them are so open that more than one answer could fit.
A page forward is the way to go unless you explicitly require the browser url to be changed. A page forward is basically faster than a redirection as it requires less steps. A page redirect is required if you want to make your views bookmarkable.
Use <h:commandLink />/<h:commandButton /> only when you need to POST the server. Later on, you'll be able to perform a page forward or a redirection depending on what the method returns. As an example:
<h:commandLink action="#{bean.processForm}" value="Submit" />
public String processForm(){
try{
save();
return "list";
}
catch(Excepcion e){
addFacesMessage("Error saving");
//Error saving the object, keep in the same view
return null;
}
}
Use <h:link outcome="list" value="Go to list" /> for pure page to page navigation within the JSF application. You can use either page forward and redirect. Use <f:param /> to pass view parameters.
<h:outputLink value="www.stackoverflow.com" /> could be used for external links to other sites (not into your application). Use <f:param /> to pass view parameters. I however prefer to use plain HTML with <a href="www.stackoverflow.com" /> myself for this case.
As for passing parameters to action methods in POST requests, you've got several options. f:setPropertyActionListener was so popular in JSF 1.x, but if you're already at 2.x I would recommend you going with EL 2.2, which allows method parameter declaration. Being able to use it depends on the application server you're using, but even if not available you could import yourself. Then, you'll be able to do things like that:
<h:commandButton action="#{bean.saveCar(currentCar)}" value="Save Car" />
Use it wherever you can, it'll make things just easier.
For the view parameters, use <f:viewParam /> too. It's the standard JSF way of parsing the parameters from the GET request, so just let the framework do the retrieving work for you!
See also:
JSF 2 link, commandLink and outputLink example
Using EL 2.2 with Tomcat 6.0.24
What is the difference between redirect and navigation/forward and when to use what?

Related

h:commandButton with an action and then page-navigation in JSF?

Is it possible to have a commandButton that executes a method of a certain backing bean and then also navigates to a different page?
I know that I could return a String in the method that the commandButton calls, but the method is also used on the target-page, meaning it's often called from that same page.
So for calls that come from the same page, the redirect would be unnecessary and I would like to avoid that.
The options that I have in mind right now:
Create a separate method for the "remote" call of the method that does the same logic and also redirects to the page
Use an additional h:button and use JavaScript so that if the commandButton is pressed, the h:button is pressed at the same time (Probably bad practice tho)
Any option I am missing? Is there any way to tell the commandButton itself that it's supposed to navigate somewhere or do I have to implement this in the backing-beans?
Your title and first sentence are 'dangerous' and sort of not on topic since to both, the answer is yes and you sort of describe (= answer) that in your second paragraph already yourself.
The real question further on effectively asks about conditional navigation. But let me state first that your solution of two methods is also not wrong if you just make sure you don't do actual work in the bean (which you should not).
Now conditional navigation is by itself not difficult
returning null (to stay on the same page) without a refresh, "" to stay on the same page with a refesh,
return the new page (with redirect).
All basic JSF which I assume you are already aware of and this just requires something to do one or the other
So then the question remains if you can
detect the page you are on when the method is executed or
pass on a parameter to the action
which in turn can be used to return null or the other new page in an if/else.
Page1:
<h:commandButton action="#{mybean.action(true))" />
Page2:
<h:commandButton action="#{mybean.action(false))" />
Bean:
public String action(boolean navigate) {
doWork();
if (navigate) {
return "page2.xhtml?faces-redirect=true";
} else {
return null;
}
And if you'd want it, you could even pass null or the page name as a parameter to the method call.
Implementing detection of the source page of the action has the advantage that in the UI you do not need any knowledge on how to navigate, you always call the same method without any parameters and each new page you'd use this action on navigates to the right page without the developer needing any knowledge.
So take you pick.
I'm not completely sure if I got you right, but you could do something like this:
<h:commandButton value="Click" action="otherPage.xhtml?faces-redirect=true">
<f:actionListener binding="#{bean.method}" />
</h:commandButton>
Keep in mind that actionListener will be fired first and after that action from commandButton. Hope it helps.
Update:
Due to the fact that there was no further thinking you can use commandButton with or without redirect.
<h:commandButton value="Click" action="{bean.method}"/>

JSF CustomeScope reset for specified view

How can I control what customscope is created during request. Right now I have solution that when I click the commandLink:
<h:commandLink action="orders">
<f:actionListener type="com.mk.web.jsf.scope.ResetScopeListener" />
<span data-shortcut="F6" class="start_link">Orders</span>
</h:commandLink>
The custom scope is destroyed and a new one is created. But the problem is that the custom scope only resets if I enter the customers action using this commandLink from above.
When I enter to customers action by directly entering the url in browser the custom scope is not reset. How can I make that is customers action is loading even by commandLink or by url then always this custom scope should be reset. Can somebody give me some advice how to achieve this?
Thanks...
Typically you use a custom scope when you want your beans to live longer than the original request but shorter than the session. Based on your scenario though, it seems to me that your custom scope is behaving slightly like a request scope. Anyway, maybe I'm not understanding your scenario fully but one thing that comes to mind is that you can use system events in case a user decides to go to the next page when they type the address directly. So suppose the user tries to go to nextPage.xhtml, it could be defined like this
<f:event type="preRenderView" listener="#{bean.performPossibleCustomScopeCleanUp}" />
<h:head>
</head>
The (pseudo) method above will fire before the view root (aka nextPage.xhtml) is rendered. Inside the method you can check to see if your custom scope is still "alive" and you will handle it as needed. Keep in mind though that this event will be fired every time the page is requested. For instance, if you click the h:commandLink performPossibleCustomScopeCleanUp will still be invoked. There are ways around that
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
Just scroll down to the section Performing business action on GET parameters of the answer
An even better approach would be to implement a custom NavigationHandler through JSF's ConfigurableNavigationHandler API so you can keep track of the URL and perform actions as needed.
I didn't dig too dig so you might actually find a better code sample. Hope this helps.

Semantics of "?faces-redirect=true" in <commandlink action=...> and why not use it everywhere

I would like to understand what are the semantics behind appending the "?faces-redirect=true" in the action property of a <h:commandlink> tag in JSF2.0. Whether with it or with out it, the application indeed navigates to the target page specified in the action. So at first glance it seems that the only effect is cosmetic, i.e. to provide feedback to the user (if he is looking at the browser's visited URL) that he has moved to a new page. But if it is so innocuous and side-effects-free I cannot see why it is not the default behaviour. I suspect that it has to do with the post-based mechanism of JSF2.0. I 've noticed when browsing through a JSF application that the URLs one sees at his browser (when ?faces-redirect=true is not used) are the ones of the "previous" "page".
meta-NB. I am behind a firewall and plagued with the "SO requires external JavaScript from another domain" issue so I apologize for the absence of formatting. I will also provide feedback on your answers in a few hours, when I can access from another domain.
Page-to-page navigation should not be performed using POST at all. You should be using normal <h:link> or <h:button> for this instead of <h:commandLink> or <h:commandButton>.
So instead of
<h:commandLink value="Next page" action=nextpage.xhtml?faces-redirect=true" />
you should actually be using
<h:link value="Next page" outcome="nextpage.xhtml" />
This has the major benefit that the website is now SEO friendly. Searchbots namely doesn't index forms.
Use the <h:commandLink> only if you need to submit a form with some user input. But more than often the result is just presented in the same page, if necesary conditionally rendered/included. Only on successful submits which absolutely needs to go to a different page (e.g. login/logout), you should indeed be sending a redirect. This is the so-called Post-Redirect-Get pattern.
See also
How to navigate in JSF? How to make URL reflect current page (and not previous one)
When should I use h:outputLink instead of h:commandLink?

Why prependId="false" in a jsf form?

I know what prependId="false" does. It set the flag so that the id of the form does not prepend the id of the form child, but why? any particular reason why you do or dont want to prepend id?
In my experience, I never use this attribute. However, in some cases it can be useful.
When you use Facelets, you can create templates or include pages inside another page. So you can imagine that a page could be included in several different pages. Take the example where the parent pages contain a form, with different id:
Page 1:
<h:form id="form1">
<ui:include src="pages/my-page.xhtml"/>
...
</h:form>
Page 2:
<h:form id="form2">
<ui:include src="pages/my-page.xhtml"/>
...
</h:form>
Now, in the my-page.xhtml, you have a <h:inputText id="foo"/>. In the first case, the real ID of the input will be form1:foo, while in the second case, it will be form2:foo. This could create complex situations if you need a direct access to this component in Javascript or in Java (using findComponent("...") method).
If you use prependId="false" (or on some components forceId="true"), the real ID will be simply foo, and then your code will be simpler as you will not have to care about the container of the input field.
However, you will have to use this attribute carefully, as you may get a duplicate ID error if you use this prepend attribute too often...
In modern jsf versions it might also break ajax, see UIForm with prependId="false" breaks <f:ajax render>
A situation where prependId=false is useful is in the login form, if you are using Spring Security, because the ids of the inputtexts have to be exactly "j_username" and "j_password". So you shouldn't put the form id before them, and using prependId=false is a good choice to acheive this.
I prefer to add prependId occasionally to make styling elements via their ID classes easier. For example, a form:
<h:form id="myform" ... >
<h:inputText id="mytext" ... />
</h:form>
Would give you an ID of myform:mytext. As the colon is reserved in CSS, you have to escape the CSS to read something like #myform\:mytext { ... } which I prefer not to do. With prependId="false" I get to use just #mytext { ... } which is much simpler & nicer to read. It also plays nicer with CSS preprocessors like LESS or Sass.
One scenario where we have to set this flag is in case of Autocomplete control of primefaces library.
I had to set this flag to false when I was trying AutoComplete control of primefaces library. I was not able to get autocomplete working but after setting this flag it worked fine. You can see this link to my question regarding this problem
WARN [Parameters] Parameters: Invalid chunk ignored. warning coming in primefaces application
In addition to making for CSS selectors easier, using prependId=false makes it easier to use JavaScript and jQuery to access specific elements.
Otherwise, without using RichFaces, to get at an elmement by id using jQuery you'll have to use an ugly escape sequence like:
jQuery("form-id\\:element-id")

Set bean property from JSF page

I need to do very simple thing - pass current URL from JSF (2.0) page to the managed bean. This is needed to get URL of the login form which will redirect user back to the current page after login. (I use GAE and the bean is the wrapping around its user service, if it does matter). Any obvious way I tried doesn't work
<c:set /> - doesn't work (w/o any error or warning message)
getter which takes current URL as a parameter - doesn't work.
So many questions and many recipes, but all are complex and not elegant. Why? May be I (as other who asking) missed a key design principle? I'll appreciate any answer - simple and straightforward recipe or explanation why not to do so.
It's available by #{request.requestURL} (or if you only want the domain-relative path, use #{request.requestURI}). Use f:param to pass it and #ManagedProperty to handle it.
<h:commandButton action="#{bean.submit}" value="submit">
<f:param name="url" value="#{request.requestURL}" />
</h:commandButton>
with
#ManagedProperty("#{param.url}")
private String url;
Noted should be that f:param inside a h:commandButton doesn't work in JSF 1.x. You'd like to use h:commandLink instead.
Update as per the comments: I understood that you wanted to "pass current URL to the managed bean". But you actually want to "access current URL in managed bean".
In that case, either replace #{param.url} by #{request.requestURL}:
#ManagedProperty("#{request.requestURL}")
private String url;
Or obtain the raw HttpServletRequest instance from under the JSF hoods by ExternalContext#getRequest():
HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
String requestURL = request.getRequestURL();

Resources