How view parameters are obtained/attached from/to the URL query string (examples) ?
Let’s dissect several simple use cases and see how view parameters works (view parameters names are not mandatory to match the request parameters passed via URL query string, but in this post we will focus on this case):
CASE 1
In index.xhtml
page we have:
<f:metadata> <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/> <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> </f:metadata> ... <h:form> Enter name:<h:inputText value="#{playersBean.playerName}"/> Enter surname:<h:inputText value="#{playersBean.playerSurname}"/> <h:commandButton value="Send" action="results?faces-redirect=true&includeViewParams=true"/> </h:form>
In results.xhtml
page we have:
<f:metadata> <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/> <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> </f:metadata> You requested name: <h:outputText value="#{playersBean.playerName}"/><br/> You requested surname: <h:outputText value="#{playersBean.playerSurname}"/>
In PlayersBean
we have:
@Named @RequestScoped public class PlayersBean { private String playerName; private String playerSurname; ... }
What is happening when application reaches in (is not important how you set the query string; you can do it manually, or navigate via
with
here) index.xhtml?playernameparam=rafael&playersurnameparam=nadal
?
- The request parameters names matches the names of the view parameters, so the view parameters take the request parameters values and, finally, stores them in the
PlayersBean
managed bean underplayerName
andplayerSurname
fields. So, roughly said, you set the managed bean fields via view parameters. - The view is rendered (HTML markup is generated and sent to browser), so you can see
rafael
andnadal
in the text inputs, since they are fetched from the managed bean properties (these are#{playersBean.playerName}
and#{playersBean.playerSurname}
). - You (as user) can manually modify these values (texts) in the text inputs (or leave them like that). Now, when you click on the
Send button, you practically submit the form with current values (the part delimitated by<h:form>
</h:form>
). So, the name and surname are submitted and override/init the current values in the data model (even if you didn’t modify them). During encoding (rendering) the view, JSF will encode view parameters also against thePlayersBean
managed bean. - Further, JSF notices that you want to attach the view parameters (you signal this as:
?faces-redirect=true&includeViewParams=true
) before navigating to the next target page (results.xhtml
). The view parameters have been evaluated against thePlayersBean
managed bean earlier in this request. So, JSF process the view parameters and attaches to the action URL the corresponding query string computed from view parameters names and values. - JSF navigates to the target URL (which now contains the query string). This is visible thanks to
faces-redirect=true
.
CASE 2
In index.xhtml
page we have:
<f:metadata> <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/> <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> </f:metadata> <h:form> Enter name:<h:inputText value="#{playersBean.playerName}"/> Enter surname:<h:inputText value="#{playersBean.playerSurname}"/> <h:commandButton value="Send" action="results?faces-redirect=true&includeViewParams=true"/> </h:form>
In results.xhtml
page we have:
<f:metadata> <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/> <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> </f:metadata> You requested name: <h:outputText value="#{playersBean.playerName}"/><br/> You requested surname: <h:outputText value="#{playersBean.playerSurname}"/>
In PlayersBean
we have:
@Named @RequestScoped public class PlayersBean { private String playerName = "roger"; private String playerSurname = "federer"; ... }
What is happening when application reaches in index.xhtml ? (no query string)
- There is no query string (no request parameters). So, the view parameters cannot be initialized from the query string, and they doesn’t set anything in
PlayersBean
also! - The view is rendered (HTML markup is generated and sent to browser), and the text inputs reflect the
roger andfederer initalization
data (these are the result of evaluating#{playersBean.playerName}
and#{playersBean.playerSurname}
). - You (as user) can modify these values in the text inputs (or not!). Now, when you click on the
Send
button, you practically submit the form (the data that belongs to the part delimitated by<h:form>
</h:form>
). So, the name and surname are submitted and override/init the current values in model (even if you didn’t modify them). During encoding (rendering) the view, JSF will encode view parameters also against thePlayersBean
managed bean. - Further, JSF notices that you want to attach the view parameters (you signal this as:
?faces-redirect=true&includeViewParams=true
) before navigating to the next target page (results.xhtml
). The view parameters have been evaluated against thePlayersBean
managed bean earlier in this request. So, JSF process the view parameters and attaches to the action URL the corresponding query string computed from view parameters names and values. - JSF navigates to the target URL (which now contains the query string). This is visible thanks to
faces-redirect=true
.
CASE 3
In index.xhtml
page we have:
<f:metadata> <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/> <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> </f:metadata> <h:link value="Send" outcome="results" includeViewParams="true"/>
In results.xhtml
page we have:
<f:metadata> <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/> <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> </f:metadata> You requested name: <h:outputText value="#{playersBean.playerName}"/><br/> You requested surname: <h:outputText value="#{playersBean.playerSurname}"/>
In PlayersBean
we have:
@Named @RequestScoped public class PlayersBean { private String playerName; private String playerSurname; ... }
What is happening when application reaches in (is not important how you set the query string; you can do it manually, or navigate via
with
here) index.xhtml?playernameparam=rafael&playersurnameparam=nadal
?
- The request parameters names matches the names of the view parameters, so the view parameters take request parameters values and stores them in the managed bean under
playerName
andplayerSurname
. So, you set the managed bean fields via view parameters. - The view is rendered (HTML markup is generated and sent to browser), so in the text inputs you can see
rafael
andnadal
texts, since they are fetched from the managed bean (these are the results of evaluating#{playersBean.playerName}
and#{playersBean.playerSurname}
). During encoding (rendering) the view, JSF will encode view parameters also against thePlayersBean
managed bean. Now, check the source code of the page and notice that the<a href>
corresponding to<h:link>
was generated as below (notice that this isfix!
). So, JSF transforms the<h:link>
into a<a href>
and attaches the query string containing the view parameters right from the initial request. TheincludeViewParams="true"
attribute causes the below link: - When you click the link, you DON’T submit any data (
<h:link>
should never be in a<h:form>
). You simply execute the above static HTML code, which is a simple GET request! - JSF navigates to the target URL via this GET (which contains the query string). There is no need for
faces-redirect=true
.
CASE 4
In index.xhtml
page we have:
<f:metadata> <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/> <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> </f:metadata> <h:link value="Send" outcome="results" includeViewParams="true"/>
In results.xhtml
page we have:
<f:metadata> <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/> <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> </f:metadata> You requested name: <h:outputText value="#{playersBean.playerName}"/><br/> You requested surname: <h:outputText value="#{playersBean.playerSurname}"/>
In PlayersBean
we have:
@Named @RequestScoped public class PlayersBean { private String playerName = "roger"; private String playerSurname = "federer"; ... }
What is happening when application reaches in index.xhtml ? (no query string)
- There are no request parameters. So, the view parameters cannot be initialized from the query string. The view parameters doesn’t set anything in the managed bean also!
- The view is rendered (HTML markup is generated and sent to browser), so you can see roger and
federer
in the text inputs, since they are fetched from the managed bean (these are the result of evaluating the#{playersBean.playerName}
and#{playersBean.playerSurname}
). During encoding (rendering) the view, JSF will encode view parameters also against thePlayersBean
managed bean (obtainingroger
andfederer
). Now, check the source code of the page and notice that the<a href>
corresponding to<h:link>
was generated as below (notice that this isfix!
). So, JSF transforms the<h:link>
into a<a href>
and attaches the query string containing the view parameters right from the initial request. TheincludeViewParams="true"
attribute causes the below link: - When you click the link, you DON’T submit any data. You simply execute the above static HTML code, which is a simple GET request!
- JSF navigates to the target URL via this GET (which contains the query string). There is no need for
faces-redirect=true
.
CASE 5
In index.xhtml
page we have:
<f:metadata> <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/> <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> </f:metadata> <h:link value="Send" outcome="results" includeViewParams="true"/>
In results.xhtml
page we have:
<f:metadata> <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/> <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> </f:metadata> You requested name: <h:outputText value="#{playersBean.playerName}"/><br/> You requested surname: <h:outputText value="#{playersBean.playerSurname}"/>
In PlayersBean
we have:
@Named @RequestScoped public class PlayersBean { private String playerName; // this is null private String playerSurname; // this is null ... }
What is happening when application reaches in index.xhtml ? (no query string)
- There are no request parameters. So, the view parameters cannot be initialized from the query string. The view parameters doesn’t set anything in the bean!
- The view is rendered (HTML markup is generated and sent to browser), so you can’t see anything in the text inputs, since they are fetched from the bean (these are
#{playersBean.playerName}
and#{playersBean.playerSurname}
which arenull
– you cannot expect to see textnull!
). During encoding (rendering) the view, JSF will encode view parameters also against thePlayersBean
managed bean (obtainingnull
). Now, check the source code of the page and notice that the<a href>
corresponding to<h:link>
was generated as below (notice that this isfix!
). So, JSF transforms the<h:link>
into a<a href>
, but there is no query string containing the view parameters because JSF sees theincludeViewParams="true"
attribute, but it cannot generate this HTML: <a href=”/…/results.xhtml?playernameparam=null&playersurnameparam=null“>Send</a> So, JSF will “ignore” thenull
values and there is no query string to attach: <a href=”/…/results.xhtml”>Send</a> - When you click the link, you DON’T submit any data. You simply execute the above static HTML code, which is a simple GET request!
- JSF navigates to the target URL via this GET (which contains the query string). There is no need for faces-redirect=true.
CASE 6 – for better understanding null values
In index.xhtml
page we have:
<f:metadata> <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/> <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> </f:metadata> <h:form> Enter name:<h:inputText value="#{playersBean.playerName}"/> Enter surname:<h:inputText value="#{playersBean.playerSurname}"/> <h:commandButton value="Send" action="results?faces-redirect=true&includeViewParams=true"/> </h:form>
In results.xhtml
page we have:
<f:metadata> <f:viewParam name="playernameparam" value="#{playersBean.playerName}"/> <f:viewParam name="playersurnameparam" value="#{playersBean.playerSurname}"/> </f:metadata> You requested name: <h:outputText value="#{playersBean.playerName}"/><br/> You requested surname: <h:outputText value="#{playersBean.playerSurname}"/>
In PlayersBean
we have:
@Named @RequestScoped public class PlayersBean { private String playerName; // this is null private String playerSurname; // this is null ... }
What is happening when application reaches in index.xhtml ? (no query string)
- There are no request parameters. So, the view parameters cannot be initialized from the query string. The view parameters doesn’t set anything in the bean also!
- The view is rendered (HTML markup is generated and send to browser), and you can see two empty text inputs (these are the results of evaluating the
#{playersBean.playerName}
and#{playersBean.playerSurname}
). You cannot expect to see text, null! - As a user, don’t type anything in these text inputs and press on the
Send
button. Practically, you will submit the form (the data that belongs to the part delimitated by<h:form>
</h:form>
). So, the name and surname (which are empty spaces) are submitted and override/init the current values in model. During encoding (rendering) the view, JSF will encode view parameters also against thePlayersBean
managed bean (will obtain empty spaces). - Further, JSF notices that you want to attach the view parameters (you signal this as:
?faces-redirect=true&includeViewParams=true
) before navigating to the next target page (results.xhtml
). The view parameters have been evaluated against thePlayersBean
managed bean earlier in this request. So, JSF process the view parameters and attaches to the action URL the corresponding query string computed from view parameters names and values. - JSF navigates to the target URL (which now contains the query string). This is visible thanks to
faces-redirect=true
.
http://localhost:8080/.../results.xhtml?playernameparam=&playersurnameparam=
Notice the values of playernameparam
and playersurnameparam!
Since you have submitted empty spaces, this is what you will see. Of course, this looks “ugly” and pretty useless. Maybe you will prefer to treat empty spaces as null
values. For this you can set in web.xml
the following context parameter:
<context-param> <param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name> <param-value>true</param-value> </context-param>
Now, clean and build the app and run it again with the same scenario. This time when you press on the
Send button, you notice this link:
- http://localhost:8080/ch2_6/faces/results.xhtml
So, no query string is reflecting the view parameters presence! Well, you just instructed JSF to treat submitted empty string as null
values. But, as you know, null
values are “ignored” when the view parameters are attached.
NOTE
The way of attaching view params can be seen in Mojarra, in com.sun.faces.application.view.MultiViewHandler
. Especially in:
// Mojarra 2.2.9, MultiViewHandler#addViewParameters() protected void addViewParameters(FacesContext ctx, String viewId, Map<String,List<String>> existingParameters) { UIViewRoot currentRoot = ctx.getViewRoot(); String currentViewId = currentRoot.getViewId(); Collection<UIViewParameter> toViewParams = Collections.emptyList(); Collection<UIViewParameter> currentViewParams; boolean currentIsSameAsNew = false; currentViewParams = ViewMetadata.getViewParameters(currentRoot); if (currentViewId.equals(viewId)) { currentIsSameAsNew = true; toViewParams = currentViewParams; } else { ViewDeclarationLanguage pdl = getViewDeclarationLanguage(ctx, viewId); ViewMetadata viewMetadata = pdl.getViewMetadata(ctx, viewId); if (null != viewMetadata) { UIViewRoot root = viewMetadata.createMetadataView(ctx); toViewParams = ViewMetadata.getViewParameters(root); } } if (toViewParams.isEmpty()) { return; } for (UIViewParameter viewParam : toViewParams) { String value = null; // don't bother looking at view parameter if it's been overridden if (existingParameters.containsKey(viewParam.getName())) { continue; } if (paramHasValueExpression(viewParam)) { value = viewParam.getStringValueFromModel(ctx); } if (value == null) { if (currentIsSameAsNew) { value = viewParam.getStringValue(ctx); } else { value = getStringValueToTransfer(ctx, viewParam, currentViewParams); } } // SO, IF VALUE IS NULL, DON'T CONSIDER THIS A VIEW PARAM if (value != null) { List<String> existing = existingParameters.get(viewParam.getName()); if (existing == null) { existing = new ArrayList<String>(4); existingParameters.put(viewParam.getName(), existing); } existing.add(value); } } }
Reference: | How view parameters are obtained/attached from/to the URL query string (examples) ? from our JCG partner Anghel Leonard at the JSF and OmniFaces Fans blog. |