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 <h:link> with <f:param> here) index.xhtml?playernameparam=rafael&playersurnameparam=nadal
?
1. 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 under playerName and playerSurname fields. So, roughly said, you set the managed bean
fields via view parameters.
2. The view
is rendered (HTML markup is generated and sent to browser), so you can see rafael
and nadal
in the text inputs, since they are fetched from the managed bean properties (these are #{playersBean.playerName}
and
#{playersBean.playerSurname}).
3. 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 the PlayersBean managed bean.
4. 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 the PlayersBean 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.
5. 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)
1. 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!
2. The view
is rendered (HTML markup is generated and sent to browser), and the text inputs
reflect the roger
and federer
initalization data (these are the result of evaluating #{playersBean.playerName} and #{playersBean.playerSurname}).
3. 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 the PlayersBean managed bean.
4. 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 the PlayersBean 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.
5. 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 <h:link> with <f:param>
here) index.xhtml?playernameparam=rafael&playersurnameparam=nadal
?
1. 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 and playerSurname.
So, you set the managed bean fields via view parameters.
2. The view
is rendered (HTML markup is generated and sent to browser), so in the text inputs you can see rafael and nadal
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 the PlayersBean 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 is fix!). So, JSF transforms
the <h:link>
into a <a
href> and attaches the query string containing the view parameters right
from the initial request. The includeViewParams="true"
attribute causes the below link:
<a
href="/.../results.xhtml?playernameparam=rafael&playersurnameparam=nadal">Send</a>
3. 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!
4. 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)
1. 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!
2. 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 the PlayersBean managed bean (obtaining roger and federer). 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 is fix!). So, JSF transforms the <h:link> into a <a href> and attaches the query string containing the view parameters right from the initial request. The includeViewParams="true" attribute causes the below link:
<a
href="/.../results.xhtml?playernameparam=roger&playersurnameparam=federer">Send</a>
3. 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!
4. 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)
1. 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!
2. 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 are null - you cannot expect to
see text null!). During encoding (rendering) the view, JSF will encode view parameters also against the PlayersBean managed bean (obtaining null). 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 is fix!). So, JSF transforms the <h:link>
into a <a
href>, but there is no query string containing the view parameters
because JSF sees the includeViewParams="true" attribute, but it
cannot generate this HTML:
<a
href="/.../results.xhtml?playernameparam=null&playersurnameparam=null">Send</a>
So, JSF will "ignore"
the null
values and there is no query string to attach:
<a
href="/.../results.xhtml">Send</a>
3. 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!
4. 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)
1. 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!
2. 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!
3. 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 the PlayersBean managed bean (will obtain empty spaces).
4. 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 the PlayersBean 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.
5. 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);
}
}
}
Niciun comentariu :
Trimiteți un comentariu