Navigate after successfull action on commandButton - jsf

I am building a JSF application using Primefaces mobile (0.9.3 with Primefaces 3.5).
I hava a login page, on this login page there is a login button defined as followed:
<p:commandButton value="#{msg['label.login']}" action="#{loginBean.login}" update="usernameMsg" />
The loginBean.login method is defined as followed:
public String login() {
try {
//do login
return "#loggedInTarget?transition=fade";
} catch (UserNotValidException e) {
//User not valid, display exception
FacesContext
.getCurrentInstance()
.addMessage(
"loginForm:username",
new FacesMessage(
FacesMessage.SEVERITY_ERROR,
"",
messageService
.getMessage("error.message.loginNotSuccessfull")));
}
return null;
}
In my main xhtml (the same in which the button is defined), I have defined the following addtional views:
<pm:view id="registerTarget">
<ui:include
src="/WEB-INF/c52bc19d58a64ae4bd12dec187a2d4b7/register.xhtml" />
</pm:view>
<pm:view id="loggedInTarget">
TEST
</pm:view>
I do it that way because I want to use transitions within Primefaces mobile. To get to the register page I use a p:button which works fine:
<p:button value="#{msg['label.register']}"
href="#registerTarget?transition=fade" />
Why is my login button not working? I also tried to put in "#loggedInTarget?transition=fade" directly as action, but this didn't work either :(
I need the action, because I have to check if the user credentials are valid. If they are valid I want to display my loggedInTarget.
How can I achieve it?
Thanks in advance!
EDIT:
The button is inside a form.

The navigation case outcome value must represent a view ID, not an URL (for sure not a hash fragment). Try with <p:button outcome="...">, and you'll see that it fails over there as well.
Your closest bet is sending a redirect.
public void login() throws IOException {
// ...
externalContext.redirect("#loggedInTarget?transition=fade");
// ...
}
This is also exactly what the <p:button href="..."> does under the covers: causing a GET request on the given URL.

Related

Form not submitting on a page with fragment 'rendered' conditions and ui:param

I'm creating a some sort of online shop with JSF. I have a product.xhtml page that displays the product by ID. I'm passing that ID as a param (.../product.xhtml?id=3) and I'm getting the ID with <ui:param name="productID" value="#{request.getParameter('id')}" />
That all works well.
Next, I'm showing and hiding certain elements in the page with <f:subview> (I've used <ui:fragment> before). The reason is that if the user deletes the ?id=3 ID parameter, the page will show an error (eg. code <f:subview id="main" rendered="#{productID != null and productID != ''}">). Another reason is that if the product belongs to the buyer, the BUY button will not appear and if the user is not authenticated the BUY button will not appear.
The problem is with the BUY button. It is in a form and the action of the button is just a simple test method (for now) from the CDI bean that prints something to the server console and redirects the user. Unfortunately, this does not work. The page (/product.xhtml) gets reloaded with no ID param.
I've tried several things like this and this and nothing is working.
I've tried using the ViewScoped and SessionScoped for my CDI bean instead of RequestScoped, but that does nothing. The ViewScoped fails to build.
I've also changed the <ui:fragment> to <f:subview>
Here's some code..
CDI bean controller
#Named
#RequestScoped
public class ProductManager {
...
public String buy(Product product) {
FacesContext context = FacesContext.getCurrentInstance();
try {
HttpSession session = Util.getSession();
User buyer = (User)session.getAttribute("user");
Date date = new Date();
System.out.println("TEST DATA: ");
System.out.println("sale product: "+product.getTitle());
System.out.println("sale buyer: "+buyer.getUsername());
System.out.println("sale date: "+date);
}
catch(Exception ex) {
System.err.println("ProductManager#buy -> "+ex.getMessage());
}
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "An error occured", null));
return "index";
}
...
}
Products.xhtml
...
<ui:param name="productID" value="#{request.getParameter('id')}" />
...
<f:subview id="buyBtn" rendered="#{user.username != login.username and login.regUser}">
<h:form style="margin-top: 30px;">
<b:navCommandLink styleClass="btn btn-info btn-block" disabled="#{!product.status}" value="Buy" action="#{productManager.buy(product)}"></b:navCommandLink>
</h:form>
</f:subview>
...
I can provide full code if needed.
What I'm expecting the code to do is that whenever I click on the BUY button, I'll get redirected to my page and the TEST DATA will be printed on server console.
After many. many attempts I solved this. I cannot find the exact explanation, as I read a ton of articles on this topic.
I used OmniFaces. Next in products.xhtml I changed h:form to o:form (from OmniFaces) and have these two set to true includeRequestParams="true" includeViewParams="true". My bean then became org.omnifaces.cdi.ViewScoped
This is also a great article

p:button with onclick javascript hook

I'm using p:button with onclick action. (I can't move to p:commandButton, because of legacy outcome navigation in faces-config.xml and custom navigation history in db):
<p:remoteCommand name="unlock_tt" actionListener="#{ttEntityMBean.unlock()}"/>
<p:button value="#{msgs['button.ok']}" outcome="#{ttEntityMBean.navigationMenuItemToRedirect.navigationRule}" onclick="unlock_tt()"/>
Generated by primefaces javascript looks like
onclick="unlock_tt(); window.open(....)"
And after clicking button, unlock_tt() in browser initiated, but immediatly broken by page redirecting, so backed java method didn't execute.
Should I make unlock_tt() or java call async to be sure it will be executed before browser leaves page?
Upd: I'm thinking to use p:commandButton, if it is possible to get to-view-id programically, like in this question:
Programmatically get navigation case <to-view-id> from faces-config.xml by outcome
<p:commandButton action="#{ttEntityBean.unlock()}"/>
public String unlock() {
//some business logic
return OutcomeResolverHelper.getRuleFor(navigationMenuItemToRedirect.navigationRule)
}
This should reduce number of requests
I'm not sure this will work but can you try this :
Instead of :
<p:remoteCommand name="unlock_tt" actionListener="#{ttEntityMBean.unlock()}"/>
<p:button value="#{msgs['button.ok']}" outcome="#{ttEntityMBean.navigationMenuItemToRedirect.navigationRule}" onclick="unlock_tt()"/>
do:
<h:panelGroup>
<f:ajax event="click" listener="#{ttEntityMBean.unlock}"/>
<p:button value="#{msgs['button.ok']}" outcome="#{ttEntityMBean.navigationMenuItemToRedirect.navigationRule}" />
<h:panelGroup>
Don't forget to remove onclick and your remotecommand.
This should wrap a panelGroup around your button. PanelGroup implements ClientBehaviorHolder so it can get f:ajax tag in its children. Thus when you click on it it should trigger the listener.
The easiest way was migration from p:button to p:commandButton, which allows to execute some java-code in action method before redirect. The core is programmical translation of navigation rule, defined in faces-config.xml to base url, usable in explisit jsf-2.0 style:
//receive base url from <to-view-id> tag
public static String resolveOutcomeRule(String ruleName) {
FacesContext context = FacesContext.getCurrentInstance();
ConfigurableNavigationHandler configNavHandler = (ConfigurableNavigationHandler)context.getApplication().getNavigationHandler();
NavigationCase navCase = configNavHandler.getNavigationCase(context, null, ruleName);
return navCase.getToViewId(context);
}
//decorate it with faces-redirect=true and our custom navigation
public static String resolveOutcome(NavigationMenuItem item) {
return resolveOutcomeRule(item.getNavigationRule()) + "?faces-redirect=true&menu=" + item.getId();
}
//controller-method
public String cancelEditTT() {
super.unlock();
return FacesUtil.resolveOutcome(getNavigationMenuItemToRedirect());
}
//new jsf button:
<p:commandButton value="#{msgs['button.ok']}" action="#{ttEntityMBean.cancelEditTT}"/>
This solution provided 1-step redirect instead of 2-step old one.

jsf 2 outputlink rendering method call without clicking

I am relatively new in jsf.
I have a jsf page someDetails.xhtml, in which I have an output link
<h:outputLink value="#{someTaskController.completeTask(taskId)}?taskId=#{taskId}">Assign Ticket</h:outputLink>
On clicking on this link, the method completeTask should be called and do something.
The problem is, when the jsf page someDetails.xhtml is opened(in browser), the method completeTask is getting called and does all the task, which should only happen on clicking the link.
What should I do ? Please help
You're using the wrong tag for your purpose, use command link instead:
<h:commandLink value="Assign Ticket" action="#{someTaskController.completeTask()}">
<f:param name="taskId" value="#{taskId}" />
</h:commandLink>
You can access taskId inside method completeTask() like this:
public void completeTask() {
Map<String,String> params =
FacesContext.getExternalContext().getRequestParameterMap();
String taskId= params.get("taskId");
// do your business action...
}

How to open a new jsf window in bean?

I am totally new about jsf page.
I want build a log in page, where you write in the correct password you will get to a new page, and if not it will show "invalid password".
now I can already validate if the password the correct using "if else" in the bean part. i figure if I can just direct to the next window it would be fine but I just cant get that part done... help !!
try this:
<h:commandButton action="page2" value="Page2" />
where page2 is the name of the page you want to show.
See also: http://www.tutorialspoint.com/jsf/jsf_page_navigation.htm
then do this:
<h:commandButton action="{YourBean.validateUser()}" />
In the bean:
public String validateUser() {
// Validate the user...
if (theUserIsValid) {
return "page2"
} else {
return null;
}
Where page2 is the name of the page you want to show when the user is validated.

h:commandButton action redirect to context root, possible?

Project context
I've created from scratch an URL rewriting with two major component :
public class URLFilter implements Filter
{
...
}
public class URLViewHandler extends GlobalResourcesViewHandler
{
...
}
The first class is used to forward clean URLs to the right view with an ID different for each page. The second class override the function getActionURL() so that h:form and ajax functionnalities continues to work.
Theses classes translate like this :
Real URL Internal URL
/ <-> page.jspx?key=1
/contact <-> page.jspx?key=2
/projects/management <-> page.jspx?key=3
etc
Current solution
My problem right now is for my user login and logout button :
<!-- Login button used if user is not logged, go to a secured page (which display error message). If he log with this button, the current page is reloaded and displayed properly. This button works perfectly -->
<h:commandButton rendered="#{pageActions.item.isPrivate}" value="#{msg.button_connect}" actionListener="#{userActions.onButtonLoginClick}" />
<!-- Login button used anywhere on public pages that redirect to user home after login, works perfectly since I haven't changed to clear url. -->
<h:commandButton rendered="#{not pageActions.item.isPrivate}" value="#{msg.button_connect}" actionListener="#{userActions.onButtonLoginClick}" action="userHome.jspx?faces-redirect=true" />
<!-- Logout button that works (it redirects at http://website.com/context-name/ but keep the ?key=1 at the end. -->
<h:commandButton value="#{msg.button_disconnect}" actionListener="#{userActions.onButtonLogoutClick}" action="page.jspx?key=1&faces-redirect=true" styleClass="button" style="margin-left: 5px;" />
My whishes
My question : Is there a better way to program the logout button since I need to redirect to context-root, currently I'm using the view name with the home page key but I would prefer 1. using a real path 2. not keep the ?key=1 at the url.
Thank you!
Final code
Based on BalusC answer, here is my final code to share to others :
#ManagedBean
#RequestScoped
public class NavigationActions
{
public void redirectTo(String p_sPath) throws IOException
{
ExternalContext oContext = FacesContext.getCurrentInstance().getExternalContext();
oContext.redirect(oContext.getRequestContextPath() + p_sPath);
}
}
<h:commandButton rendered="#{not pageActions.item.isPrivate}" value="#{msg.button_connect}" actionListener="#{userActions.onButtonLoginClick}" action="#{navigationActions.redirectTo(userSession.language.code eq 'fr' ? '/profil/accueil' : '/profile/home')}" />
Since I don't need the key when I have the path, it is even more better, thank you again BalusC to put me on the right track! Sent a small donation :)
This isn't possible with (implicit) navigation. The / is unfortunately not a valid JSF view ID.
Use ExternalContext#redirect() instead. Replace
action="page.jspx?key=1&faces-redirect=true"
by
action="#{userActions.redirectToRootWithKey(1)}"
with
public void redirectToRootWithKey(int key) throws IOException {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.redirect(ec.getRequestContextPath() + "?key=" + key);
}

Resources