Preserving query-string parameters using request scoped beans [duplicate] - jsf

This question already has answers here:
Retaining GET request query string parameters on JSF form submit
(2 answers)
Closed 6 years ago.
Whenever I make an ajax call, my URL param expires. The workaround I have done is to pass a param request inside every button like in:
<p:commandLink value="Submit" actionListener="#{mybean.dosomething}">
<f:param name="foo" value="#{mybean.bar}"/>
</p:commandLink>
However, in some scenarios I can't do the above workaround. For example when I'm using primefaces rowEditEvent:
<p:ajax event="rowEditCancel" listener="#{mybean.onCancel}"/>
The param expires when I make this ajax call and results in error before invoking #{mybean.onCance} as I'm reading datatable's data from the param in URL.
So how can I maintain the param value when I make such an ajax call?
PS: mybean is ViewScoped
Problem Extension:
The <o:form> has solved part of the problem, but now I can't send params inside the form for dynamic image streaming inside the table. See the following:
<p:dataTable value="#{mybean.data}" var="var">
<p:column headerText="Thumbnail">
<p:graphicImage value="#{streamer.thumbnail}">
<f:param name="id" value="#{var.id}"/>
</p:graphicImage>
</p:column>
</p:dataTable>
Streamer Bean (RequestScoped):
public StreamedContent getThumbnail() throws IOException {
FacesContext context = FacesContext.getCurrentInstance();
if (context.getRenderResponse()) {
return new DefaultStreamedContent();
}
else {
String string = context.getExternalContext().getRequestParameterMap().get("id");
Long id = Long.parseLong(idString);
MyImage img = (MyImage) service.find(MyImage.class, id);//get resource
StreamedContent sc = new DefaultStreamedContent(img.getInputStream(), "image/jpg", img.getName());
return sc;
}
}
Here string is always null and parameter is not passed resulting in error

Register it as a <f:viewParam>
<f:viewParam name="foo" value="#{mybean.bar}" />
and instead of <h:form>, use OmniFaces <o:form> with includeViewParams="true" (which uses under the covers a custom ViewHandler implementation which automatically collects all view parameters and appends them to the outcome of the getActionURL() method which is used as form action URL):
<o:form includeViewParams="true">
...
</o:form>
This way all view params are included in the form's action URL and will therefore end up as request parameters the usual way without the need to fiddle with <f:param> and being clueless inside <p:ajax> (and <f:ajax>).

Related

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.

why getQueryString() does not work in jsf backing bean with h:commandButton

I have build a login snippet of code that is on the top of menu bar. If a user is in any of the page by navigating and presses all of sudden the login button, I'll like to see that person authenticated and at the same time stay on the page where he originally comes from. so I used this on the backing bean:
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
then if there is let say mypage.htm?a=2&b=2 get a=2&b=2 with the request.getQueryString()
but getQueryString returns null, how can I can have the original URL entirely from the backing bean?
It returned null because the command button does not submit to an URL which includes the query string. Look in the generated HTML output of <h:form>. You'll see that generated HTML <form action> does not include the query string at all.
You need to pass the current query string yourself as a request parameter in the login link/button.
Grab the current request URI and query string and add it as from parameter in login link/button (note: use a normal link/button, it doesn't need to be a command link/button):
<h:button value="login" outcome="/login">
<f:param name="from" value="#{request.requestURI}#{empty request.queryString ? '' : '?'}#{request.queryString}" />
</h:button>
In login page, set the from parameter as view scoped bean property:
<f:metadata>
<f:viewParam name="from" value="#{login.from}" />
</f:metadata>
In login action method, redirect to it:
public void submit() throws IOException {
User user = userService.find(username, password);
if (user != null) {
// ... Do your login thing.
FacesContext.getCurrentInstance().getExternalContext().redirect(from);
} else {
// ... Do your "unknown login" thing.
}
}
<h:commandButton> sends a POST request to the server, more specifically to the Faces Servlet, and the JSF lifecycle starts. As you may know, you can't access to the query string parameters in a POST request.
In case you want to send a GET request from your form or retrieve the query string parameters when accessing to the page, you must map each expected query parameter using <f:viewParam>
<f:metadata>
<f:viewParam id="a" name="a" value="#{bean.a}" />
<f:viewParam id="b" name="b" value="#{bean.b}" />
</f:metadata>
More info:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
ViewParam vs #ManagedProperty(value = "#{param.id}")
You can also solve this by changing the <h:form> wrapping the <h:commandButton> to an omnifaces form with includeRequestParams set to true: <o:form includeRequestParams="true">. See BalusC's response to this question.

How can I maintain param on ajax call? [duplicate]

This question already has answers here:
Retaining GET request query string parameters on JSF form submit
(2 answers)
Closed 6 years ago.
Whenever I make an ajax call, my URL param expires. The workaround I have done is to pass a param request inside every button like in:
<p:commandLink value="Submit" actionListener="#{mybean.dosomething}">
<f:param name="foo" value="#{mybean.bar}"/>
</p:commandLink>
However, in some scenarios I can't do the above workaround. For example when I'm using primefaces rowEditEvent:
<p:ajax event="rowEditCancel" listener="#{mybean.onCancel}"/>
The param expires when I make this ajax call and results in error before invoking #{mybean.onCance} as I'm reading datatable's data from the param in URL.
So how can I maintain the param value when I make such an ajax call?
PS: mybean is ViewScoped
Problem Extension:
The <o:form> has solved part of the problem, but now I can't send params inside the form for dynamic image streaming inside the table. See the following:
<p:dataTable value="#{mybean.data}" var="var">
<p:column headerText="Thumbnail">
<p:graphicImage value="#{streamer.thumbnail}">
<f:param name="id" value="#{var.id}"/>
</p:graphicImage>
</p:column>
</p:dataTable>
Streamer Bean (RequestScoped):
public StreamedContent getThumbnail() throws IOException {
FacesContext context = FacesContext.getCurrentInstance();
if (context.getRenderResponse()) {
return new DefaultStreamedContent();
}
else {
String string = context.getExternalContext().getRequestParameterMap().get("id");
Long id = Long.parseLong(idString);
MyImage img = (MyImage) service.find(MyImage.class, id);//get resource
StreamedContent sc = new DefaultStreamedContent(img.getInputStream(), "image/jpg", img.getName());
return sc;
}
}
Here string is always null and parameter is not passed resulting in error
Register it as a <f:viewParam>
<f:viewParam name="foo" value="#{mybean.bar}" />
and instead of <h:form>, use OmniFaces <o:form> with includeViewParams="true" (which uses under the covers a custom ViewHandler implementation which automatically collects all view parameters and appends them to the outcome of the getActionURL() method which is used as form action URL):
<o:form includeViewParams="true">
...
</o:form>
This way all view params are included in the form's action URL and will therefore end up as request parameters the usual way without the need to fiddle with <f:param> and being clueless inside <p:ajax> (and <f:ajax>).

JSF passing a bean to a page

I have created a little controller of sorts that depending on the input could send it to a number of pages each with a different backing bean. I do this because one page, which takes a case number, verifies that case number and then passes it on to any number of pages which need a valid case number.
Here is my caseSelector page:
<h:form id="form1">
<h:inputText value="#{caseSelectorBean.caseNumber}"/>
<h:inputHidden value="#{caseSelectorBean.nextPage}"/>
<h:commandButton action="#{caseSelectorBean.gotoNext}" value="submit"/>
</h:form>
my bean correctly verifies the casenumber and fills out the bean for the next page and forwards it to that page.
//CaseSelectorBean
public String gotoNext() {
logger.debug("In the caseChooser, going to xpage");
logger.debug("caseNum=" + caseNumber);
if(!validateCaseNumber(caseNumber)) {
return "caseNotFound";
}
if(nextPage.equals("page1")) {
CaseDAO caseDAO = new CaseDAO();
caseInfo = caseDAO.getCaseInfo(caseNumber);
CaseInfoBean caseInfoBean = new CaseInfoBean();
caseInfoBean.setCaseInfo(caseInfo);
caseInfoBean.setCaseNumber(caseNumber);
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(
"caseInfoBean", caseInfoBean);
}
logger.debug("done with gotoNext(), returning " + nextPage);
return nextPage;
}
problem is when I try to access the bean from the next page, it's empty. I can see via the constructor, it created a new one instead of using the one in the session. How would I make the page use the bean I created for it?
Thanks!
The ManagedProperty with #{param} works only for request scoped beans. Use f:viewParam for broader scopes:
<f:metadata>
<f:viewParam name="caseNumber" value="#{caseSelectorBean.caseNumber}"/>
</f:metadata>
Then you need only a simple property caseNumber with getter and setter in your bean.
   

Sending values with <h:commandbutton> not working working properly in jsf

When i tried to send some values with h:commandButton... i recieved null value in the bean...
my xhtml code is:
<p:commandButton action="#{favouriteAction.setFavourite}" value="Add as Favorite" rendered="#{favouriteBean.favouriteButton}">
<f:setPropertyActionListener target="#{favouriteAction.ngoID}" value="#{InsertDataDaoService.ngo_id}"></f:setPropertyActionListener>
</p:commandButton>
In the backing bean i just tried to print the value which i passed with my command button,but it becomes null...
In favouriteAction.java(My backing Bean)
public Integer ngoID;
public Integer getNgoID() {
return ngoID;
}
public void setNgoID(Integer ngoID) {
this.ngoID = ngoID;
}
public String setFavourite(){
System.out.println("Ngo id: "+ngoID);
System.out.println("Ngo id: "+getNgoID);
return "";
}
In console i dint get any exceptions, my o/p is
Ngo id: 0
Ngo id: 0
that is null, and it doesnt get passed..
The <f:setPropertyActionListener> is evaluated during the request of the form submit, not during the request of displaying the form. So if its value #{InsertDataDaoService.ngo_id} is not preserved for that request, then it will fail.
You have basically 2 options:
Ensure that #{InsertDataDaoService.ngo_id} is preserved for the request of the form submit. How exactly do to that depends on the functional requirements which are not clear from the question. But generally, putting the #{InsertDataDaoService} bean in the view scope by #ViewScoped and making sure that you aren't doing any business job in the getter method should be sufficient.
Replace <f:setPropertyActionListener> by <f:param> with #ManagedProperty.
<p:commandButton action="#{favouriteAction.setFavourite}" value="Add as Favorite" rendered="#{favouriteBean.favouriteButton}">
<f:param name="ngoID" value="#{InsertDataDaoService.ngo_id}" />
</p:commandButton>
with
#ManagedProperty("#{param.ngoID}")
private Integer ngoID;
This way the value will be retrieved (and inlined as part of a JavaScript helper function of the commandButton) during the request of displaying the form, not evaluated during the request of submitting the form.
See also:
How can I pass selected row to commandLink inside dataTable?
Have you checked that the value of InsertDataDaoService.ngo_id is not NULL?
Try to replace it with a constant value. Does it work?

Resources