Using a list of currencies in the form of strings as follows.
<p:selectOneMenu id="currency"
value="#{currencyRateBean.currency}"
onchange="changeCurrency([{name: 'currency', value: this.value}]);">
<f:selectItems var="row"
value="#{currencyBean.currencies}"
itemLabel="#{row}"
itemValue="#{row}"/>
</p:selectOneMenu>
Along a <p:remoteCommand>
.
<p:remoteCommand ignoreAutoUpdate="true"
name="changeCurrency"
partialSubmit="true"
process="@this"
update="@none"
action="#{currency.currencyAction}"/>
The managed bean setting a currency value being passed through the above <p:remoteCommand>
as a parameter to a JavaScript function.
@Named
@RequestScoped
public class Currency {
@Inject
@HttpParam
private String currency;
@Inject
private CurrencyRateBean currencyRateBean;
public Currency() {}
public String currencyAction() throws MalformedURLException, IOException {
try (Scanner scanner = new Scanner(new URL("http://www.exchangerate-api.com/INR/" + currency + "/1?k=FQRxs-xT2tk-NExQj").openConnection().getInputStream(), "UTF-8");) {
currencyRateBean.setCurrencyRate(scanner.nextBigDecimal());
currencyRateBean.setCurrency(currency);
} catch (UnknownHostException | ConnectException e) {}
return FacesContext.getCurrentInstance().getViewRoot().getViewId() + "?faces-redirect=true&includeViewParams=true";
}
}
The supplied currency value is then set to another session scoped managed bean CurrencyRateBean
from within the action method currencyAction()
above which finally makes a redirect based on the current value of viewId
along with includeViewParams=true
which is important.
Now, the story changes, when #{currencyRateBean.currencies}
has been changed to have a list of composite objects which has been a list of Strings so far.
The following scenario will not work with includeViewParams=true
which is significant.
<p:selectOneMenu value="#{currencyRateBean.currencyHolder}">
<f:selectItems var="row" value="#{currencyBean.currencies}"
itemLabel="#{row.currency}"
itemValue="#{row}"/>
<p:ajax event="change"
listener="#{currency.currencyAction}"
partialSubmit="true"
process="@this"
update="@none"/>
</p:selectOneMenu>
public void currencyAction() throws IOException {
// ...
FacesContext facesContext = FacesContext.getCurrentInstance();
String viewId = facesContext.getViewRoot().getViewId();
ExternalContext externalContext = facesContext.getExternalContext();
externalContext.redirect(externalContext.getRequestContextPath() + viewId + "?includeViewParams=true");
}
includeViewParams=true
has been added for decoration only. It is not going to work.
Since listener
in <p:ajax>
is incapable of making a redirect based on a navigation case outcome as done by action
of <p|h:commandXxx>
, ExternalContext#redirect()
has to be used anyway.
<p:remoteCommand>
can be used on complete of <p:ajax>
but this will involve two round trips to the server unnecessarily, first to set the currency value to the associated managed bean and then to make a redirect.
How to make a redirect with includeViewParams=true
in the example given?
Like faces-redirect=true
, includeViewParams=true
works only in navigation outcomes, not in "plain" URLs which you pass to ExternalContext#redirect()
.
Use NavigationHandler#handleNavigation()
.
FacesContext context = FacesContext.getCurrentInstance();
String outcome = viewId + "?includeViewParams=true";
context.getApplication().getNavigationHandler().handleNavigation(context, null, outcome);
Or, with OmniFaces.
Faces.navigate(viewId + "?includeViewParams=true");
A dubious alternative is to collect all view parameters yourself and convert them to query string so you can use ExternalContext#redirect()
anyway. This is easier with OmniFaces.
Faces.redirect(viewId + "?" + Servlets.toQueryString(Faces.getViewParameterMap()));