3

I'm trying to open a new browser tab with a JSF view (in a portlet, deployed in Liferay) from within a view backed by a ViewScoped bean. Using a normal action redirect kills the bean. I've tried the method provided here and here, but unfortunately without success.

The button looks more or less like this:

<p:commandButton value="#{msg.label}" onclick="target='_blank'" 
                 action="#{sessionScopedBean.action(param)}" ajax="false" />

Moving the target='_blank' to the form attribute did not help. I've tried both returning null and void with no success. Changing ajax to true broke the navigation, didn't open a new tab but also did not kill the ViewScoped bean.

The action method content looks like this:

public void action(String param) throws IOException {
   //some business logic

   FacesContext.getCurrentInstance().getExternalContext().redirect("viewName.xhtml");
}

The view does not contain tag handlers like <c:if test="..."> or <ui:include src="...">. It did contain a <ui:repeat id="..." value="#{viewScopedBean.collection}" var="..." varStatus="..."> tag, but removing it changed noting. The form is enclosed in <ui:composition> and <ui:define> tags.

The view I redirect to has no connection with the ViewScoped bean. Any ideas? :)

Community
  • 1
  • 1
MarcelK
  • 283
  • 2
  • 4
  • 11

1 Answers1

3

The view scope broke because you're with the redirect action basically instructing the client to fire a brand new GET request on the given URL. You should instead be returning null or void and conditionally render the results in the same view.

See also:


The solution was already given in the links you found: put the data of interest in the flash scope before redirect and obtain them from the flash scope in the bean associated with target view. If this isn't working for you for some reason, an alternative would be to generate an unique key (java.util.UUID maybe?) and store it in the session scope as key associated with some data you'd like to retain in the redirected request,

String key = UUID.randomUUID().toString();
externalContext.getSessionMap().put(key, data);

and then pass that key along as request parameter in the redirect URL

externalContext.redirect("nextview.xhtml?key=" + key);

so that you can in the postconstruct of the bean associated with the target view obtain the data:

String key = externalContext.getRequestParameterMap().get("key");
Data data = (Data) externalContext.getSessionMap().remove(key);
// ...
Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thx So, does this mean that I misunderstood the questions/answers I've provided links to? i.e. it's impossible to open a new tab with a different view without "killing" the ViewScoped bean from the first view? It seemed that it was the use case from the questions. As for the second part of your answer, I have no problem with passing the data. I just want to preserve the original view (I try to avoid making major changes in some buggy legacy code). The use case is as follows: user fills a form (view one - has unsubmitted data), and can view a document that has been uploaded before (second view) – MarcelK Sep 03 '13 at 13:33
  • I didn't see those links. The answers in those links suggests to use flash scope for this. Using the flash scope is indeed also one way, this has essentially the same effect as above session approach. But after time I know that the flash scope has some issues in combination with certain Mojarra versoins and as you're using a portlet (which is a quite restrictive environment) I just wanted to exclude trouble with flash scope. – BalusC Sep 03 '13 at 13:36
  • Ok, so it's impossible to cheat the system and make a ViewScoped bean survive a redirect in a new tab? I got this idea because you wrote "Indeed, the view scoped bean in the initial tab/window get killed by returning a `String` navigation case outcome. You would like to return `null` or `void` to keep it alive." and people initially wrote about missing data when returning to the first tab :) – MarcelK Sep 03 '13 at 13:50
  • 2
    A view scoped bean lives as long as the JSF view. A JSF view lives as long as you're postbacking to the same view (by returning `null`/`void`). Once you send a redirect (this is completely regardless of the target being in same or new tab), then the view is destroyed. Just return `null` or `void` and conditionally render the results in the same view. It'll work (this is also completely regardless of the target being in same or new tab). – BalusC Sep 03 '13 at 13:54
  • So to sum things up: the void/null method will work if the Faces.redirect() is to the same view, but wont if it's to a different one. So it's not a way to bypass the ViewScoped bean behavior. I'll try some other way to deal with this bug in the legacy code (like the one you suggested). Thanks for your answer. As for the second part of your (now edited) answer, it was not what I was trying to do (passing data to the new view/bean was not a problem). The problem was loosing the data in the first view, which was still active from user's perspective. – MarcelK Sep 03 '13 at 14:16
  • No. Do not send a redirect at all. It creates a new GET request and thus also a new view. Just return `void`/`null` without any redirect and conditionally render results in the same view. – BalusC Sep 03 '13 at 14:18