JSF Missing get parameters in url [duplicate] - jsf

This question already has answers here:
Retaining GET request query string parameters on JSF form submit
(2 answers)
Closed 8 years ago.
After days of googling I must give up and ask for help. I have xhtml page with jsf tags. Whole page is controlled by PageController.java. Important thing is that controller takes item Id and page number from url parameters. Beside that I have small form which contains current logged user information or username and password input if no one is logged in. I would like have possibility to log in or log out user using that form. But when I call either user logIn() or logOut() function from user controller parameters from url just missing. I've already tried using h:commandbutton and h:commandlink as well. I managed to send parameters via f:param tag but they still missing from url (that's problem because I need bookmarking here). tags like h:link, h:button or h:outputLink cannot be used because they don't allow me to call controller function on click.
Thanks for any help.
Edit:
I forgot to mention, I've tried to solve this via f:ajax. Problem is after log in I cannot log out without refreshing page. This same with other direction.
Edit II:
I will show sample code illustrating the problem. In my project I have template.html which is simple page template containing header, footer, aside menu, and main content. It probably have not influence at the problem I've met. My code looks like that:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<f:metadata>
<f:event type="preRenderComponent" listener="#{PageController.init()}"/>
</f:metadata>
<ui:composition template="/template.xhtml">
<ui:define name="aside-menu">
<h:form id = "sidemenuform">
<h:panelGrid rendered="#{usersController.isLoggedIn()}" columns="1">
<h:commandLink value="LogOut" >
<f:param name="orderId" value="${PageController.groupId}"/>
<f:param name="page" value="${PageController.PageNumber}"/>
</h:commandLink>
</h:panelGrid>
<h:panelGrid rendered="#{!usersController.isLoggedIn()}" columns="1">
<h:outputLabel for="usernameInput" value="#{bundle.LogginUsernameLabel}: "/>
<h:inputText id="usernameInput" value="#{usersController.username}"/>
<h:outputLabel for="passwordInput" value="#{bundle.LogginPasswordLabel}:">
</h:outputLabel>
<h:inputSecret id="passwordInput" value="#{usersController.password}"/>
<h:commandButton action="#{usersController.login()}">
<f:param name="orderId" value="${PageController.GroupNumber}"/>
<f:param name="page" value="${PageController.PageNumber}"/>
</h:commandButton>
</h:panelGrid>
</h:form>
</ui:define>
<ui:define name="body">
<div id="navBar">
<ul>
<li class="previousPage">
<h:commandLink action="#{PageController.previous}" rendered="#{PageController.pagination.hasPreviousPage}">
</h:commandLink>
</li>
<li class="nextPage">
<h:commandLink action="#{PageController.next}" rendered="#{PageController.pagination.hasNextPage}">
</h:commandLink>
</li>
</ul>
</div>
<ui:repeat value="#{PageController.items}" var="item" varStatus="status">
<!-- List of items in group divided into pages -->
</ui:repeat>
</ui:define>
</ui:composition>
PageController.previous and PageController.next functions working well because they returning url with GET parameters as string which looks like:
Page?groupId=1&PageNumber=2&faces-redirect=true
But userController knows nothing about them, neither about page which we currently show. So functions Login and Logout can not return url. Both functions return void. The problem is that when I fire these functions after page reload my address url changed from
/Project/faces/groups/Page.xhtml?groupId=534?pageNumber=1
To
/Project/faces/groups/Page.xhtml
And then init function fail. How to prevent this behavior??

h:link and h:button create javascript links, there's no chance to use them for invoking server side methods. What you need is to POST the server with user credentials and later on invoke a REDIRECT if the login process is correct. Your best is to use a page controller for the current view and a user controller which has the session info for the logged user. You can easily inject one bean into the other one:
#ManagedBean
#SessionScoped
public class UserController{
public boolean doLogin(String email, String password){
//Do your internal request here
}
}
#ManagedBean
#ViewScoped
public class PageController{
//You'll need a setter for this
#ManagedProperty(value=#{userController})
private UserController userController;
public String doLogin(){
if (userController.doLogin(email, password)){
return "page?groupId=" + groupId + "PageNumber=" +
pageNumber +"faces-redirect=true";
}
else{
//Add some faces message describing the error
}
}
}
That way you make the process transparent for the view, which will only acess page controller methods.

Try using f:viewParams inside your f:metadata tags:
<f:metadata>
<f:viewParam name="orderId" value="${PageController.groupId}"/>
<f:viewParam name="page" value="${PageController.PageNumber}"/>
</f:metadata>
and append ?includeViewParams=true to the url returned by userController.login() and userController.logout(). This should append the view parameters to the url automatically.

Related

Omnifaces TagAttribute Id cannot be used as id

I have the following Facelet Taglib:
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:o="http://omnifaces.org/ui"
xmlns:h="http://java.sun.com/jsf/html">
<o:tagAttribute name="id"/>
<h:panelGroup id="#{id}" layout="block" styleClass="idTest">
#{id}
</h:panelGroup>
</ui:composition>
The taglib.xml looks like this:
<tag>
<tag-name>idTest</tag-name>
<source>resources/myProject/tags/idTest.xhtml</source>
</tag>
And the code, where it is used is simply:
<myProject:idTest/>
How can it be, that the following HTML is being rendered:
<div class="idTest">
j_ido489794984_4bf870cd
</div>
Why does my PanelGroup have no id? The id is generated like it is expected based on the documentation of o:tagAttribute, since the content of the div is rendered. But as id it does not work. Why?
This is indeed confusing.
The documentation literally says:
... it will autogenerate an unique ID in the form of j_ido[tagId] where [tagId] is the <o:tagAttribute> tag's own unique ID.
But the actual behavior is more like so:
... it will override any autogenerated ID into the form of j_ido[tagId] where [tagId] is the <o:tagAttribute> tag's own unique ID.
In other words, when JSF itself needs to render the id attribute of a HTML element, usually because it's required by some internal logic further down in the chain, such as <f:ajax> and friends, and there is no explicit ID specified on the tag like so <x:someTag id="fixedId" />, then JSF will by default autogenerate one in the form of j_id[autoIncrementInteger]. But this will go wrong on tagfiles because the autoIncrementInteger may get bumped further by one on each postback depending on the JSF impl and view state configuration used. The <o:tagAttribute> simply attempts to ensure this way that the autogenerated ID stays the same on each postback.
When you edit your test tagfile to add <f:ajax>,
<h:panelGroup id="#{id}" layout="block" styleClass="idTest">
<f:ajax event="click" />
</h:panelGroup>
then you'll see that the generated <div> has an id, because this is technically required by <f:ajax>.
<div id="j_ido-1512689859_13a7e7e3" class="idTest"
onclick="mojarra.ab(this,event,'click',0,0)">
</div>
Or when you swap <h:panelGroup> for e.g. a <h:form> or whatever component which always requires an ID in the client side,
<h:form id="#{id}" styleClass="idTest">
<ui:insert />
</h:form>
then you'll also see that it's generated.
<form id="j_ido-1512689859_13a7e7e3" name="j_ido-1512689859_13a7e7e3" method="post" action="/test.xhtml" class="idTest" enctype="application/x-www-form-urlencoded">
<input type="hidden" name="j_ido-1512689859_13a7e7e3" value="j_ido-1512689859_13a7e7e3" />
...
</form>
In other words, the feature is working fine, but it was simply not used in your specific case because JSF didn't consider it necessary to generate it.
I've in the meanwhile updated the documentation.

JSF 2.3 Facet in Composite Component with wrong ID

I have the following simple code in a composite component (using Mojarra 2.3.9 / Primefaces 7):
<composite:implementation>
<h:form id="form">
<composite:insertChildren />
<ui:fragment rendered="#{!empty cc.facets.actions}">
<div class="actions">
<composite:renderFacet name="actions" />
</div>
</ui:fragment>
</div>
</h:form>
</composite:implementation>
And the following part is used in a page, trying to fill the composite form with life:
<cc:compForm id="mySpecialForm">
<f:facet name="actions">
<p:commandButton
id="myBtn"
value="Submit"
process="#form"
update="#form">
</p:commandButton>
</f:facet>
</cc:compForm>
The form and all the children are rendered correctly and working quite well. But the button in the renderFacet block has - in my opinion - a wrong client ID, because instead of:
mySpecialForm:form:myBtn
the button only gets the following clientId:
mySpecialForm:myBtn
This leads to an error rendering the page:
Cannot find component for expression "#form" referenced from
"mySpecialForm:myBtn".:
org.primefaces.expression.ComponentNotFoundException: Cannot find
component for expression "#form" referenced from
"mySpecialForm:myBtn".
Am i doing something wrong or is this a bug in JSF/Primefaces? I also tried to configure the componentType to an #FacesComponent extending from UIForm, but in this case no form will be rendered at all.
Update 1:
I tried to create a "minimal, reproducible example (reprex)" like mentioned by Kukeltje. All what is needed are those 2 Parts in a web application (both files under resources):
cc/compForm.xhtml:
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:composite="http://xmlns.jcp.org/jsf/composite">
<composite:interface name="compForm" displayName="A composite form">
<composite:facet name="actions" />
</composite:interface>
<composite:implementation>
<h:form id="form">
<composite:insertChildren />
<composite:renderFacet name="actions" />
</h:form>
</composite:implementation>
</html>
compFormTest.xhtml:
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:cc="http://xmlns.jcp.org/jsf/composite/cc">
<cc:compForm id="mySpecialForm">
<h:inputText id="inputParam" value="" />
<f:facet name="actions">
<h:commandButton id="myBtn" value="Test" />
</f:facet>
</cc:compForm>
</html>
All todo is call the .xhtml page: http:localhost/YOUR_APP/compFormTest.xhtml.
After using it (at least with Mojarra JSF implementation), the input field has the following correct client ID mySpecialForm:form:inputParam. But the command button retrieves another client ID outside the form: mySpecialForm:myBtn, what is a bug from my point of view, regarding the JSF VDL: " ... will be rendered at this point in the composite component VDL view.".
But as i downstriped the example files, it is clearly not a primefaces problem, because the wrong client ID is also included, if using the standard h:commandButton component.
Perhaps someone can use the mentioned 2 files above in a MyFaces environment to check if the behaviour differs or is the same?
Or has someone a workaround in mind? Using an additional #FacesComponent and moving the button from facet to the right spot under the form leads to the following "funny" duplicate ID error:
"Cannot add the same component twice: mySpecialForm:form:myBtn" (at least the client ID was what i expected in the first place)

JSF & HTTP Requests [duplicate]

This question already has an answer here:
How to navigate in JSF? How to make URL reflect current page (and not previous one)
(1 answer)
Closed 6 years ago.
let the following code fragment contains a link to items.xhtml that lists item names and enables the user to select an item in order to view its details:
<?xml ... ?>
<!DOCTYPE ... >
<html ... >
<h:head>
<title>Welcome</title>
</h:head>
<h:body>
<h:outputLink value="#{facesContext.externalContext.requestContextPath}/faces/client/items.xhtml">Items</h:outputLink>
</h:body>
</html>
When the user clicks the link named Items, then the request line states that the resource being requested is /javaee7-training/faces/client/items.xhtml and the desired action that should be applied to it is GET. That is fine.
The following code shows a fragment of items.xml:
<?xml ... ?>
<!DOCTYPE ...>
<html ...>
<h:head>
<title>items</title>
</h:head>
<h:body>
<h:form prependId="false">
<h:selectOneRadio value="#{bean.itemId}"
layout="pageDirection">
<c:forEach items="#{bean.items}" var="itm">
<f:selectItem itemValue="#{itm.itemno}"
itemLabel="#{itm.itemname}" />
</c:forEach>
</h:selectOneRadio>
<h:commandButton value="Details" action="item" />
</h:form>
</h:body>
</html>
Now when the user clicks the button named Details, then the request line states that the resource being requested is /javaee7-training/faces/client/items.xhtml and the desired action that should be applied to it is POST. I donĀ“t expected this result, because when the user clicks the button named Details, then the requested resource is /javaee7-training/faces/client/item.xhtml and the desired action is GET.
Could anyone explain me this result and why I am wrong?
Thank you in advance
<h:commandButton> Always fire a post request because it is supposed to inside the <h:form>. And forms are always submitted as POST request in JSF arena. If you need to fire GET request use <h:button> instead of <h:commandButton>.
Usage Example:
<h:button value="Click Me!!" outcome="item"> <!--viewId is item insted of items-->
<f:param name="item1" value="itemVal1" />
<f:param name="item2" value="itemVal2" />
</h:button>

Retaining GET request query string parameters on JSF form submit

I have 3 pages:
main.xhtml
agreement.xhtml
generated.xhtml
The agreement.xhtml needs two parameters to load correctly: serviceId and site. So, a normal url looks like this: /app/agreement.xhtml?site=US&serviceId=AABBCC.
I have this button on agreement.xhtml
<h:form>
<h:commandButton value="Generate License File" action="#{agreement.generateMethod}" />
</h:form>
The #RequestScoped bean #{agreement} has this method:
public String generateMethod(){
.......
return "generated";
}
I need that, on click, the generateMethod() method is executed, and after it's done, the user is redirected to the generated.xhtml page. What's happening is that, on click, the page browser sends the user to /app/agreement.xhtml and, since it's not sending the parameters site and serviceId, it crashes.
I tried making the generateMethod() return a "generated?faces-redirect=true", but still nothing. Any ideas?
Your concrete problem is caused because a JSF <h:form> submits by default to the current request URL without any query string. Look closer at the generated HTML output, you'll see
<form action="/app/agreement.xhtml" ...>
You'd thus explicitly need to include those request parameters yourself. There are several ways to solve this. If you weren't sending a redirect, then you could just add them as hidden inputs to the JSF form.
<h:form>
<input type="hidden" name="site" value="#{param.site}" />
<input type="hidden" name="serviceId" value="#{param.serviceId}" />
...
</h:form>
Only, those parameters won't reappear in URL in browser's address bar. This isn't a problem if you're only using using ajax on the same page. The <h:inputHidden> is by the way not suitable as it will confusingly lose its value when a conversion or validation error occurs on the form.
In order to get them to reappear in URL, you need <f:viewParam> and includeViewParams. In order to get includeViewParams to work, you need to declare the following in both the source page agreement.xhtml ...
<f:metadata>
<f:viewParam name="site" value="#{agreement.site}" />
<f:viewParam name="serviceId" value="#{agreement.serviceId}" />
</f:metadata>
... and the target page generated.xhtml:
<f:metadata>
<f:viewParam name="site" value="#{generated.site}" />
<f:viewParam name="serviceId" value="#{generated.serviceId}" />
</f:metadata>
Now you can send a redirect including the view parameters as follows:
public String generateMethod() {
// ...
return "generated?faces-redirect=true&includeViewParams=true";
}
Do note that the bean should be #ViewScoped in order to keep those parameters alive between opening the page with the form and submitting the form, also on validation errors. Otherwise, when sticking to a #RequestScoped bean, you should be retaining them as <f:param> in the command components:
<h:commandButton ...>
<f:param name="site" value="#{generated.site}" />
<f:param name="serviceId" value="#{generated.serviceId}" />
</h:commandButton>
There's no way to set them for <f:ajax> inside input components, your bean should then really be #ViewScoped.
Alternatively, if you happen to use JSF utility library OmniFaces already, then you could also just replace the <h:form> by <o:form> as follows (see also showcase example):
<o:form>
That's basically all. This will generate a <form action> with current query string included.
<form action="/app/agreement.xhtml?site=US&serviceId=AABBCC" ...>
Those request parameters are then just available in the request parameter map of the form submit. You don't need additional metadata/viewparams and you also don't need to send a redirect and your bean can be kept #RequestScoped, if necessary.
public String generateMethod() {
// ...
return "generated";
}
See also:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
How to navigate in JSF? How to make URL reflect current page (and not previous one)
Your generateMethod would have to return
return "generated?site=US&serviceId=AABBCC&faces-redirect=true";
You can even replace & with & but escape it in your xhtml.
In your generated.xhtml you can catch the parameters that are being passed with <f:viewParam> like this
<f:metadata>
<f:viewParam name="site" value="#{yourBean.site}"/><!--Make sure you have a setter-->
<f:viewParam name="serviceId" value="#{yourBean.serviceId}"/><!--Make sure you have a setter-
</f:metadata>
<h:head>

How to create links relative to the right context?

I have this composition :
<!DOCTYPE html>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:panelGroup rendered="#{empty userc.userb.user.id}">
<h:panelGrid columns="2" >
<h:outputLink value="system/register.xhtml">Register</h:outputLink>
<h:outputLink value="system/login.xhtml">Login</h:outputLink>
</h:panelGrid>
</h:panelGroup>
</ui:composition>
If the user click in Login the page is redirect to system/login.xhtml, which is correct, but if the user, click in Login again, it is redirect to system/system/login.xhtml.
I know a solution for this, which BalusC help me out a long time ago:
<base href="#{fn:replace(request.requestURL, fn:substring(request.requestURI, 1, fn:length(request.requestURI)), request.contextPath)}/" />
It solve my problem, but if I have some ManageBean instancied when I click in some link the bean it's invalidate.
How mantain the url path in every link page and keep the session in the Managed beans ?
Use <h:link> instead. JSF will append the right context path and FacesServlet mapping.
<h:link value="Register" outcome="/system/register" />
<h:link value="Login" outcome="/system/login" />
See also:
Communication in JSF 2.0 - Implicit navigation

Resources