I have a navigation rule that looks like this:
<navigation-rule>
<display-name>forgotPwd</display-name>
<from-view-id>/login/forgotpwd.jsf</from-view-id>
<navigation-case>
<from-outcome>pass</from-outcome>
<to-view-id>/login/pwdresetcomplete.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>fail</from-outcome>
<to-view-id>/login/forgotpwd.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
So I do this to trigger the navigation rule:
Navigate to /login/forgotpwd.jsf?uuid=fed8b3f7-ed33-4941-8306-3d2afbb8d1d0
This page has the following form:
<h:form id="resetForm">
<!-- Omitting the login fields -->
<h:commandButton type="submit" id="resetPassword" value="Save" styleClass="btn small" action="#{registration.resetPassword}" >
<f:param name="uuid" value="#{facesContext.externalContext.requestParameterMap.uuid}" />
</h:commandButton>
</h:form>
(Takes the uuid passed as a query string parameter, and will call the registration.resetPassword function.)
Here is the registration.resetPassword java code:
(Details omitted for brevity)
public String resetPassword() {
// If good
return "pass";
// If bad
return "fail"
}
Problem: When it returns "pass", the navigation rule is not firing. It goes back to /login/forgotpwd.jsf instead of /login/pwdresetcomplete.jsf
Is this because it has the UUID parameter appended to it? Why is my navigation not firing?
Is there some log4j logging that I can trigger to see why this isn't working?
<from-view-id>/login/forgotpwd.jsf</from-view-id>
The from-view-id must be the view ID, not the virtual URL. So, it should not contain .jsf extension.
<from-view-id>/login/forgotpwd.xhtml</from-view-id>
By the way, request parameters are in the view available by #{param}. This saves some characters as compared to #{facesContext.externalContext.requestParameterMap}.
<f:param name="uuid" value="#{param.uuid}" />
See also implicit objects in EL. Another by the way, it's not exactly clear which JSF version you're using, but since JSF 2.0 (which I guess you're using as you're on Facelets already) you don't need the navigation case XML hell anymore. You can just return the (relative and/or extension-less) to-view-id directly from the action method.
public String resetPassword() {
// If good
return "pwdresetcomplete";
// If bad
return "forgotpwd"
}
This way you can get rid of the whole <navigation-rule>. See also implicit navigation.
I added these two rules... now it works. Not sure which one did it but using the from-action made it a lot more happy.
<navigation-rule>
<display-name>forgotPwd</display-name>
<from-view-id>/login/forgotpwd.xhtml</from-view-id>
<navigation-case>
<from-action>#{registration.resetPassword}</from-action>
<from-outcome>pass</from-outcome>
<to-view-id>/login/pwdresetcomplete.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-action>#{registration.resetPassword}</from-action>
<from-outcome>fail</from-outcome>
<to-view-id>/login/forgotpwd.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<display-name>forgotPwd</display-name>
<from-view-id>/login/forgotpwd.jsf</from-view-id>
<navigation-case>
<from-action>#{registration.resetPassword}</from-action>
<from-outcome>pass</from-outcome>
<to-view-id>/login/pwdresetcomplete.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-action>#{registration.resetPassword}</from-action>
<from-outcome>fail</from-outcome>
<to-view-id>/login/forgotpwd.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
Related
I noticed that since I defined an onstart behavior in my button, navigation rules are ignoring me.
My button:
<p:commandButton
id="btnSend"
value="#{txt['button.send']}" process="#form"
immediate="true"
action="#{controller.goToSend}"
onstart="generate();">
</p:commandButton>
and my faces-config:
<navigation-rule>
<from-view-id>*</from-view-id>
<navigation-case>
<from-action>#{controller.goToSend}</from-action>
<from-outcome>success</from-outcome>
<to-view-id>/private/menu/lists/index.xhtml</to-view-id>
<redirect include-view-params="true">
<view-param>
<name>back</name>
<value>1</value>
</view-param>
</redirect>
</navigation-case>
</navigation-rule>
If I remove the onstart attribute it works.
What do you think?
I have created a JSF Flow. My definition is as follows:
<flow-definition id="registration">
<initializer>#{registrationBean.initializeFlow}</initializer>
<start-node>personalDetails</start-node>
<view id="personalDetails">
<vdl-document>/registration/personal-details.xhtml</vdl-document>
</view>
<navigation-rule>
<from-view-id>*</from-view-id>
<navigation-case>
<from-outcome>registration</from-outcome>
<to-view-id>/registration/personal-details.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/registration/personal-details.xhtml</from-view-id>
<navigation-case>
<from-outcome>next</from-outcome>
<to-view-id>/registration/cultural-details.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/registration/cultural-details.xhtml</from-view-id>
<navigation-case>
<from-outcome>previous</from-outcome>
<to-view-id>/registration/personal-details.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>next</from-outcome>
<to-view-id>/registration/profile-details.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/registration/profile-details.xhtml</from-view-id>
<navigation-case>
<from-outcome>previous</from-outcome>
<to-view-id>/registration/cultural-details.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>next</from-outcome>
<to-view-id>/registration/preview.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/registration/preview.xhtml</from-view-id>
<navigation-case>
<from-outcome>previous</from-outcome>
<to-view-id>/registration/profile-details.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/registration-complete.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<flow-return id="cancel">
<from-outcome>cancel</from-outcome>
</flow-return>
<finalizer>#{registrationBean.finalizeFlow}</finalizer>
</flow-definition>
When I move through the Faces Flow, there are no problems in the actual flow pages, but the template is outputting a strange error:
Unable to find matching navigation case from view ID '/registration/personal-details.xhtml' for outcome 'registration'
Where /registration/personal-details.xhtml is the current file name and registration is the name of the current Flow Scope.
The rendered link I have in my menu is also rendered as:
Sign Up
Outside the flow, or:
Sign Up: This link is disabled because a navigation case could not be matched.
Inside the flow.
At the same time, the Glassfish log is giving me this:
Warning: JSF1064: Unable to find or serve resource, /registration/registration.xhtml.
This registration.xhtml doesn't exist, but I thought I could override the default file name by changing the <start-node> in the config.
Is there a way I can keep my file structure with the non-default value and get this working? What exactly is the problem?
It seems that the first view must be registration.xhtml (or <FLOW_NAME>.xhtml in general), so I can get rid of the error message, and technically this "answers" my question, but I would prefer to be able to name my views as I see fit, and not have to default if possible, so I'll hold out for a while on selecting an answer in case anybody has any better input on this matter.
You can rename the start page (or use any arbitrary faces page in any directory) using the FlowBuilder inside a #Producer method instead of XML configuration.
See: https://blog.oio.de/2014/02/12/a-comprehensive-example-of-jsf-faces-flow/
This would require switching from face-config.xml to code based configuration... Faces/directory based configuration is not very flexible and requires sticking to the standard convention.
I have a scenario where I have to invoke another bean and registered bean attributes before rendering the JSF page as JSF page have reference to bean attributes for rendering response.
I am looking for best option to implement such scenario. I tried to represent required scenario in form of pseudo-code for better understanding.
Page A --> have button "View Detail" bind with BeanOne.getDetail() -- Scope is session.
BeanOne.getDetail()
process the request
- call the backend method to get detail
- if (type == XYZ) then invoke BeanTwo.getTypeXYZ() and redirect as per navigation rule defined for BeanTwo.getTypeXYZ()
- else if (type == RST) then invoke BeanThree.getTypeRST() and redirect as per navigation rule defined for BeanThree.getTypeRST()
BeanTwo.getTypeXYZ()
process the request
call the backend method to get detail
set this.attributeB = value returned from backend method.
navigate to Page B
BeanTwo.getTypeRST()
process the request
call the backend method to get detail
set this.attributeC = value returned from backend method.
navigate to Page C
Something like having bean method name in navigation rule
<navigation-rule>
<from-view-id>Page A</from-view-id>
<navigation-case>
<from-action>#{beanOne.getDetail}</from-action>
<from-outcome>xyz</from-outcome>
<to-view-id>#{beanTwo.getTypeXYZ}</to-view-id>
</navigation-case>
<navigation-case>
<from-action>#{beanOne.getDetail}</from-action>
<from-outcome>rst</from-outcome>
<to-view-id>#{beanTwo.getTypeRST}</to-view-id>
</navigation-case>
</navigation-rule>
Did you try using navication-rule in faces-config.xml
<navigation-rule>
<navigation-case>
<from-outcome>XYZ</from-outcome>
<to-view-id>pageB.xhtml</to-view-id>
<redirect/>
</navigation-case>
<navigation-case>
<from-outcome>RST</from-outcome>
<to-view-id>pageC.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
in bean
getTypeXYZ() {
...
NavigationHandler nh = facesContext.getApplication().getNavigationHandler();
nh.handleNavigation(facesContext, null, "XYZ");
}
I would like to redirect to another page following the upload of a single file. The source xhtml and destination xhtml are in the same directory. I have tried returning a String in the fileUploadListenerMethod:
public String loadFills(FileUploadEvent event) throws Exception {
return "treeOrderRequestStatus";
}
but this does not result in a redirect.
I have also tried accomplishing it via faces-config.xml:
<navigation-rule>
<from-view-id>/userpages/manualDataLoad.xhtml</from-view-id>
<navigation-case>
<from-action>#{manualDataLoader.loadFills}</from-action>
<to-view-id>/userpages/treeOrderRequestStatus.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/userpages/manualDataLoad.xhtml</from-view-id>
<navigation-case>
<from-outcome>treeOrderRequestStatus</from-outcome>
<to-view-id>/userpages/treeOrderRequestStatus.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
and neither of these worked.
Finally, I have tried executing a link triggered by the uploadcomplete event:
<rich:fileUpload fileUploadListener="#{manualDataLoader.loadFills}"
maxFilesQuantity="1" createTempFiles="false" uploadLabel="Import Fill Data"
addLabel="Select Fill File">
<a4j:ajax event="uploadcomplete" execute="redirect"/>
</rich:fileUpload>
<h:link id="redirect" outcome="treeOrderRequestStatus"/>
and still no dice. In each of these attempts I have tried multiple different combinations of specifying the destination page (e.g.: including the .xhtml extension, including the directory, ...) but have not yet succeeded.
What am I doing wrong?
The execute attribute tag is to define the components that will be sent in the request to the server. You can try clicking a link/button using the uploadcomplete JavaScript method from the <rich:fileUpload> tag component:
<rich:fileUpload fileUploadListener="#{manualDataLoader.loadFills}"
maxFilesQuantity="1" createTempFiles="false" uploadLabel="Import Fill Data"
addLabel="Select Fill File">
<a4j:ajax event="uploadcomplete" onbegin="document.getElementById('yourFormName:redirect').click();" />
</rich:fileUpload>
<h:commandButton id="redirect" action="treeOrderRequestStatus" style="display: none"
tabindex="-1" />
Also, it would be better if you erase those navigation rules in your faces-config.xml to keep the file clean of needless configurations.
Framework: JSF 2.0.
My problem: I have a page, called login.xhtml. Whenever someone view that page, it invoked a filter, called Authentication filter. The filter will check, if user already loged in, it will be redirected to default based on user's role (for example: Admin will go to "admin/admin.xhtml", student will be redirected to "user/user.xhtml").
My solution: Using the JSF Navigation
My config:
faces-config.xml
<navigation-rule>
<from-view-id>*</from-view-id>
<navigation-case>
<from-outcome>AD</from-outcome>
<to-view-id>/admin/admin.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>US</from-outcome>
<to-view-id>/user/user.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
The redirection using navigation:
public static void redirectUsingNavigation(String from, String outCome) {
FacesContext facesContext = FacesContext.getCurrentInstance();
facesContext.getApplication().getNavigationHandler().handleNavigation(facesContext, from, outCome);
}
My question:
When i run the redirectUsingNavigation("*","AD") or redirectUsingNavigation(null,"AD"), it does not redirect me to admin.xhtml (i'm still on login.xhtml). How to fix this?
Any framework support me on this problem?
That code is not redirecting at all. It's just forwarding the request. It's just using the same request object for a different target page). A real redirect instructs the browser to send a new request on the given Location header. You see this change being reflected back in browser address bar.
You need to either add faces-redirect=true parameter to the outcome to trigger the redirect (this is particularly useful if you're using implicit navigation instead of verbose navigation cases):
navigationHandler.handleNavigation(facesContext, from, outCome + "?faces-redirect=true")
Or add <redirect/> to the navigation cases if you want them to always take place:
<navigation-rule>
<from-view-id>*</from-view-id>
<navigation-case>
<from-outcome>AD</from-outcome>
<to-view-id>/admin/admin.xhtml</to-view-id>
<redirect/>
</navigation-case>
<navigation-case>
<from-outcome>US</from-outcome>
<to-view-id>/user/user.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
Last but not least, you need to ensure that handleNavigation() is called before the response is committed, otherwise it won't work at all and your server logs would be littered with IllegalStateException: response already committed errors. You can make use of <f:event type="preRenderView"> to invoke a bean action before the response is committed.
See also:
jsf navigation question
Hit a bean method and redirect on a GET request