Redirecting to an external URL in a new tab and performing an action in backing bean at the same time - jsf

I'm working at a jsf application that at a certain time need to open an external page in a new tab, leaving the first page active. I need to find a way to make the application perform, in a single button click:
a redirect to an external URL in a new tab
an action which disables the button itself in the original page
I've tried using an <outputLink /> but it has no action attribute.
I've tried using a <commandLink />but it's unable to redirect outside.
I've also tried a <commandLink /> with target="_blank" and a redirection coded in the backing bean:
<h:commandLink onclick="submit();" target="_blank" action="#{myBean.execute}" disabled="#{myBean.linkDisabled}" value="external link" />
and, in the backing bean:
public String execute() {
linkDisabled = true;
try{
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
externalContext.redirect(Constants.EXTERNAL_URL);
} catch(Exception e){
throw new FacesException("Redirection failed");
}
return THIS_PAGE;
}
A page is opened in a new tab but it's the current page instead of the that with URL Constants.EXTERNAL_URLand the button is still enabled. No error message is shown. Any suggestion?
Thanks in advance, Andrea

Your question is confusing. You're mixing "link" and "button". You basically need to disable the element in the client side by JavaScript because the response does not return to the current browser window/tab, but to a different browser window/tab. In JavaScript, it's easy to disable a button, but not a link. For simplicity, I'll assume that you mean and can use a <h:commandButton> and that this is the only button in the current form.
To solve your problem, you need to remove onclick="submit()" (because it's disturbing the default behaviour) and you need to remove the navigation outcome (it should not matter, but you never know in some JSF impls) and you need to disable the button by JS in the client side.
All with all your code should look just like this:
<h:form target="_blank">
<h:commandButton
value="external"
action="#{myBean.execute}"
onclick="setTimeout('document.getElementById(\'' + this.id + '\').disabled=true;', 50);" />
</h:form>
And
public void execute() throws IOException {
FacesContext.getCurrentInstance().getExternalContext().redirect(Constants.EXTERNAL_URL);
}
The setTimeout() is necessary because the button is otherwise disabled before the form data is sent which would cause that the button's own name=value pair won't be sent as request parameter and so JSF won't invoke any action.

why don't you use action="someaction" and map it in faces-config.xml to go to another page?

Related

How to use h:outputLink without destroying the bean

Im using a especial protocol (SIP) to open a softphone trough my xhtml page using something like this
<h:outputLink value="sip:123456" />
But is destroying my bean, leaving the page useless, is there a workaround for this? any ideas would be apreciated
pd:i also trying with primefaces.
UPDATE
What scope is your bean?
Is a viewscope, and i dont have to pass any parameters, this is a special protocol sip: , what it does is that it opens a program called softphone
How would you do this is normal html?
I corrected the title thanks, in normal html would be something like this <a href="sip:3378984" > call </a>
When will your bean be destroyed? When the page is shown or if you
click the link? But, you can't click the link because there is no
content to render. How do you check the destroyed bean? Which bean?
when i hit the link it goes to my #Predestroy method, and it opens the softphone program (there is no page to show), after i hit the link the page becomes unusuable, like the links, buttons etc, wont work
i also used primefaces commandlink
<p:commandLink value="prime link" action="#{testBean.redirect()}"/>
public void redirect() throws IOException {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
externalContext.redirect("sip:123456");
}
So after some reasearch and some comments from previous users i created a script to open the new window and close it after some milliseconds
function clicktocallwindowf(number) {
var wnd = window.open("sip:" + number);
setTimeout(function () {
wnd.close();
}, 10);
return false;
}
<p:commandLink onclick="clicktocallwindowf(#{phonebean.number})" styleClass="Fs16 icon-phone-1"/>
Hope this helps somebody, thank you guys for the comments it gave me more direction!

Navigate to previous page with JSF

I use a backing bean where I can save a form and navigate to this page.
public String saveHere() {
return Faces.getViewId() + "?faces-redirect=true&includeViewParams=true";
}
How do I navigate to the previous page with OmniFaces?
My save action should result in the page where I have the "edit" button.
Typically a list-view (with edit-button next to very item)
or another page with (with edit-button to this item)
Thanks!
You need the page id you're navigating from somewhere. At first look, keeping it in Session seems to be a good idea, however, this could remain in coflict if you're navigating in multi-tab (sharing same Http Session through different browser tabs).
Having said that, the most proper solution for your case is to pass a view param to your edition view telling where you're coming from. That should be as easy as this:
<h:button outcome="edit" value="Edit registry">
<f:param name="pageFrom" value="#{view.viewId}" />
</h:button>
After that, in your edition view bind this param to your bean:
<f:metadata>
<f:viewParam name="pageFrom" value="#{editBean.pageFrom}" />
</f:metadata>
And just redirect to that view in your EditBean.java after saving, supposing it is #ViewScoped:
public String saveHere() {
//save here
return pageFrom + "?faces-redirect=true&includeViewParams=true";
}
That's the most straight forward way to achieve what you ask for. However, you must take into account the page where you're coming from remains to be a view parameter, so the end user could type what he wants there. With this behaviour, he could achieve being redirected to some other page rather than the one expected. If you want to avoid that, go with the flash scope instead of view parameters.
See also:
Get current page programmatically

How to open a blank page for an external url with JSF, if target url must be initialy retained from server?

Primefaces 3.5.14, Omnifaces 1.6
Is there a component in JSF/Prime that looks like a button, but that allows to open a new blank page with an external link ? So unlike a normal link, on click the new page URL must be initialy retained from server and just then new page must be opened. Like a commanButton with an action attribute, where a string result of an action method determine the new url.
Of course I can use p:button as in Styling one JSF external link to make it look like a button but in that case I must update link generated by p:button, depending on actions made on the page every time. This is not always desirable.
You need target="_blank" on the parent form in order to submit into a new window and you need to invoke ExternalContext#redirect() (or OmniFaces' Faces#redirect()) in the action method in order to let the target window send a new GET request on the given URL. Importantly, when you use <p:commandButton> instead of <h:commandButton>, you also need to set ajax="false" because it doesn't respect target="_blank".
Thus, so:
<h:form target="_blank">
<p:commandButton value="Submit" action="#{bean.submit}" ajax="false" />
</h:form>
with
public void submit() throws IOException {
String redirectURL = determineItSomehow();
Faces.redirect(redirectURL);
}

CommandButton open new tab with FlashScope parameters

How can I open new tab when user clicks p:commandButton? I also want to pass some parameters to new page using FlashScope. Here's the code:
<h:form>
<p:commandButton value="open new tab" action="#{myBean.newTab}"/>
</h:form>
public String newTab() {
Faces.setFlashAttribute("foo", bar);
return "otherView";
}
On the otherView page I use f:event type="preRenderView" to read Flash parameters.
Two notes:
I need to use FlashScope, not URL parameters.
If possible, I don't want to change newTab() and preRenderView() methods.
Thanks for help
Use target="_blank" on the form to tell the browser that the synchronous response of the form should be presented in a new (blank) tab/window. You only need to turn off ajax behaviour of the <p:commandButton> in order to make it a synchronous request.
<h:form target="_blank">
<p:commandButton value="open new tab" action="#{myBean.newTab}" ajax="false" />
</h:form>
No changes are necessary in the backing beans, it'll just work as you intented. I would only recommend to use POST-Redirect-GET pattern in the action method.
return "otherView?faces-redirect=true";
Otherwise the new tab would show the URL of the originating page and a F5 would re-invoke the POST. Also, this way the flash scope is also really used as it is been designed for (if you didn't redirect, just storing in request scope was been sufficient).
Update: as per the comments, the view scoped bean in the initial tab/window get killed this way. by returning a String navigation case outcome. That's right, if you'd like to keep the view scoped bean alive, replace the navigation case by a Faces#redirect() call (assuming that it's indeed OmniFaces which you're using there for Faces#setFlashAttribute()). You only need to set Flash#setRedirect() to true beforehand to instruct the flash scope that a redirect will occur.
public void newTab() throws IOException {
Faces.setFlashAttribute("foo", bar);
Faces.getFlash().setRedirect(true);
Faces.redirect("otherView.xhtml");
}

further continuing of double press

In a previous question BalusC gave me good advice on how a button, in place of a commandButton is useful for non ajax navigation. In particular it updates the destination address in the http: position which is useful for the user to bookmark a page.
I tried to use this information to my advantage until I came upon a problem. In a button I tried to use outcome="#{backing.something}" to find out that it gives me a null result. This looks like a timing problem in that action="#{}" is evaluated only when the button is pressed whereas outcome apparently wants a fixed string which gets checked when the page is loaded.
So I went back to commandButton with ajax="false". This has a problem that my navigation address is the page I came from, not the one I am navigating to. This is the wrong bookmark for the user.
I appreciate all the help I have received in stackoverflow on my learning exercise.
Ilan
The <h/p:button outcome> is not intented to invoke a bean action method, but to contain the outcome string directly. Any EL in there is evaluated immediately as a value expression. So the method behind it would immediately be invoked when you just open the page containing the <h/p:button>.
There are in your particular case basically two ways to invoke a bean action method on navigation. If you need to invoke it before the navigation takes place and the action isn't intented to be re-invoked everytime when the enduser reopens/reloads the GET request, then make it a POST-Redirect-GET request. It's a matter of adding faces-redirect=true to the outcome value in query string syntax.
E.g.
<p:commandButton action="#{bean.submit}" ... />
with
public String submit() {
// ...
return "nextpage?faces-redirect=true";
}
This way the browser will be redirected to the target page after POST, hence the enduser will see the target URL being reflected in the address bar.
Or if you need to invoke the action everytime when the enduser reopens/reloads the GET request, do the job in the (post)constructor or preRenderView listener method of the request/view scoped backing bean instead.
E.g.
<p:button outcome="nextpage" ... />
with
#ManagedBean
#RequestScoped
public class NextpageBacking {
public NextpageBacking() {
// In constructor.
}
#PostConstruct
public void onPostConstruct() {
// Or in postconstructor (will be invoked after construction AND injection).
}
public void onPreRenderView() {
// Or before rendering the view (will be invoked after all view params are set).
}
// ...
}
The pre render view listener method needs to be definied as follows in the nextpage
<f:event type="preRenderView" listener="#{nextpageBacking.onPreRenderView}" />
See also:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
Communication in JSF 2.0 - Processing GET request parameters

Resources