JSF Pass a map in ViewScoped Bean between two pages - jsf

I have two different pages. In the first page I have a Map in a ViewScoped Bean, and in the second page there is another ViewScoped Bean that needs the Map in the first page. How can I pass the Map between the two pages?
I've thought of transforming the Map into a JSON string, and passing through <f:viewParams>, but that might make the url too long.
Any ideas will be appreciated!

Given that the second page is been opened by a PRG request, your best bet is to make use of the flash scope. You can put flash scoped attributes in the map as available by ExternalContext#getFlash() and you can get them in the next request by the very same ExternalContext#getFlash() map (which is in EL scope also available by #{flash}).
So, e.g. this should do: in the action method of backing bean of first page:
public String submit() {
// ...
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.getFlash().put("data", data);
return "page2?faces-redirect=true";
}
And then a property of backing bean of second page (page2):
#ManagedProperty("#{flash.data}")
private Map<String, String> data; // Setter required.
Or, alternatively, the postconstruct of backing bean of second page:
private Map<String, String> data;
#PostConstruct
public void init() {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
data = (Map<String, String>) ec.getFlash().get("data");
}

Related

JSF h:outputLink call bean method before GET

I need to send some parameters from one xhtml to another, but I don't want these parameters to appear in the URL. How to do that? I could use p:commandLink, but then I don't know how to open the destination page from the bean method. The destination page should be accessible by friendly URL, not by xhtml name.
This code will open the page /users/view. How can I send parameters without them appearing in the URL?
<h:outputLink value="/users/view">
<h:outputText value="#{entry.employee}" />
</h:outputLink>
Ignoring the strange design, you could use put the data in the flash scope and send a redirect in a <h:commandLink> action method:
public void view() throws IOException {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.getFlash().put("employee", employee);
ec.redirect(ec.getRequestContextPath() + "/users/view");
}
And then in the backing bean associated with the target page:
#PostConstruct
public void init() {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
employee = (Employee) ec.getFlash().get("employee");
}
See also:
Pass an object between #ViewScoped beans without using GET params
What is the difference between redirect and navigation/forward and when to use what?

How to evaluate which page to show in PostConstruct?

Imagine this situation:
I have a SessionScoped bean (named TheSessionBean) that is a ManagedProperty of another bean (named AnotherBean).
#ManagedBean
#ViewScoped
public class AnotherBean implements Serializable {
#PostConstruct
public void init() {
//Evaluate here!
}
#ManagedProperty(value = "#{theSessionBean}")
private TheSessionBean theSessionBean;
//Getter and Setter...
}
I need to evaluate the value of the ManagedProperty (theSessionBean) in order to know if a page can be showed without the user interaction and before the page is shown.
As I understand this must be evaluated in the PostConstruct method (so I can get the value of the Session of the ManagedProperty).
TheSessionBean has only a String property named permission.
So first of all I need to know if:
theSessionBean.getPermission() == null in order to redirect to a page named one
theSessionBean.getPermission().equals("two") in order to redirect to a page named two
More evaluations...
The problem is that the PostConstruct method must be void and I need to redirect to the corresponding page.
How can I do that?
You can use ExternalContext.redirect in your PostConstruct method.
if (someCondition)) {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.redirect(ec.getRequestContextPath() + "/one");
}
Best practice is to use JSF system events- PreRenderViewEvent PostConstructApplicationEvent PreDestroyApplicationEvent
JSF events

How to pass bean property from one view to another view

I'm using JSF 2.1 and Primefaces:
I have a view scoped managed bean with a managed property and a method that set something on other view scoped managed bean and forward to other page referencing that managed bean:
#ManagedBean
#ViewScoped
public class HelloMB {
#ManagedProperty("otherMB")
private OtherMB other;
public String changeOtherMB() {
otherMB.setAnyObject(new Object());
return "otherPage.xhtml";
}
}
#ManagedBean
#ViewScoped
public class OtherMB {
private Object o;
public void setAnyObject(Object o) {
this.o = o;
}
}
So, when otherPage is rendered o is null.
You have idea how could I solve this? How can I retain an Object in a #ViewScoped managed bean and keep it live on other page without using #SessionScoped?
The view scope is destroyed and recreated once you navigate to a different JSF view. You know, the view scope lives as long as you're interacting with the same JSF view. In this particular case you effectively end up with two instances of the #{otherMB} managed bean during one request. One instance which is used by the source view and another instance which is used by the destination view.
As the second view is created within the very same request, you could just pass it as a request attribute.
#ManagedBean
#ViewScoped
public class HelloMB implements Serializable {
public String changeOtherMB() {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.getRequestMap().put("anyObject", anyObject);
return "otherPage.xhtml";
}
}
#ManagedBean
#ViewScoped
public class OtherMB {
private Object anyObject;
#PostConstruct
public void init() {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
this.anyObject = ec.getRequestMap().get("anyObject");
}
}
I however wonder if you're aware about the importance of idempotent vs non-idempotent requests. Perhaps you actually need a "plain vanilla" link without the need to invoke a view scoped bean action method. See the last "See also" link below for an extensive example on that.
See also:
How to choose the right bean scope?
How to navigate in JSF? How to make URL reflect current page (and not previous one)
Creating master-detail pages for entities, how to link them and which bean scope to choose

How do I force a managed bean to initialize first?

I have a navigation managed bean for each user.
and I need it to initialize first before any other bean because a value is required from the bean.
May I know how do I perform that?
I have tried eager="true" but it doesn't work.
any quick and easy solution via faceconfig would be greatly appreciated.
Just perform the desired initialization job in bean's #PostConstruct.
#PostConstruct
public void init() {
// Here.
}
It'll be invoked when the bean is injected/referenced from another bean for the first time.
The eager=true works only on application scoped beans.
From what I see you should reference the other bean. Let's assume a have a utility class that can pull a bean from the context.
Basically ->
//Bean A
public void doSomething()
{
String required = Utility.getBeanB().getRequiredValue();
use(required);
}
...
//Bean B
public String getRequiredValue()
{
return "Hi, I'm a required value";
}
I have several large web apps that have a "Session Bean" that stores stuff like user preferences, shared objects etc... and this method works perfectly. By using a reference to the bean you eliminate the need to chain the initialization. That method will always DEPEND on the method in the other bean, thus guaranteeing the order of initialization.
There's a variety of ways to access the bean but I usually go through the EL route ->
Get JSF managed bean by name in any Servlet related class
Best of luck, I try to stay "functionally pure" when I can--and I hope that get's a laugh considering the language!
Here's some cool hax for ya, in case other solutions aren't working for you due to various circumstances...
Let's say I have a class Alpha that I want initialized first:
public class Alpha {
#PostConstruct
public void init() {
}
}
I can put the following method in Alpha:
#ManagedBean(name = "Alpha", eager = true)
public class Alpha {
public static void requireAlpha() {
FacesContext context = FacesContext.getCurrentInstance();
Object alpha = context.getApplication().evaluateExpressionGet(context, "#{Alpha}", Object.class);
System.out.println("Alpha required: " + alpha.toString());
}
#PostConstruct
public void init() {
}
}
Then, in any classes that are initializing too early, simply call:
Alpha.requireAlpha();
// ...
// (Code that requires Alpha to be initialized first.)
And if there's a ChildAlpha class that extends Alpha that you really want to be initialized (rather than the parent), make sure to use "ChildAlpha" instead, in both the name="" and the EL Expression ("#{}").
See here for more infos: Get JSF managed bean by name in any Servlet related class

Iceface 2.0 commandLink partial submit doesn't work

I have a page which takes in request params for place, then generate information,
for example, http://example.com/xxx/weather.jsf?place=california.
The purpose of doing this is to let user bookmark the link.
In the weather.jsf, there are two outputtext and a commandlink:
Humidity : <ice:outputText value="#{weatherBean.humidity}"/>
Visibility : <ice:outputText value="#{weatherBean.visibility}"/>
<ice:commandLink id="likeButton"
value="Like"
actionListener="#{weatherBean.doLike}" />
In the managedBean:
#ManagedBean(name="weatherBean")
#RequestScoped
public class WeatherBean
{
String humidity;
String visibility;
int numLike;
#PostConstruct
public void init()
{
System.out.println("init called");
HttpServletRequest request= (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
String place = request.getParameter("place");
setHumidity(WeatherDao.getHumidity(place));
setVisibility(WeatherDao.getVisibility(place));
setNumLike(GeneralDao.getNumLike());
}
public void doLike(ActionEvent event)
{
System.out.println("doLike called");
GeneralDao.addNumberLike();
}
}
Alright, the page generated perfectly.
However, when I click the doLike commandLink,
it always triggers the init method first, then call doLike method.
Since the request param is empty, all the other values reset.
Is there any way to prevent a refresh of the page or calling of init method?
I tried partialsubmit or immediate, but no luck.
Your bean is #RequestScoped, so after executing the JSF lifecycle, your bean instance is lost, until the next request comes in, at which point you get a new instance of your bean, and the PostContruct re-executes.
Try changing the scope of your bean to something longer lived, like #ViewScoped.

Resources