My JSF Books/Videos My JSF Tutorials OmniFaces/JSF PPTs
JSF 2.3 Tutorial
JSF Caching Tutorial
JSF Navigation Tutorial
JSF Scopes Tutorial
JSF Page Author Beginner's Guide
OmniFaces 2.3 Tutorial Examples
OmniFaces 2.2 Tutorial Examples
JSF Events Tutorial
OmniFaces Callbacks Usages
JSF State Tutorial
JSF and Design Patterns
JSF 2.3 New Features (2.3-m04)
Introduction to OmniFaces
25+ Reasons to use OmniFaces in JSF
OmniFaces Validators
OmniFaces Converters
JSF Design Patterns
Mastering OmniFaces
Reusable and less-verbose JSF code

My JSF Resources ...

Java EE Guardian
Member of JCG Program
Member MVB DZone
Blog curated on ZEEF
OmniFaces is an utility library for JSF, including PrimeFaces, RichFaces, ICEfaces ...

[OmniFaces Utilities] - Find the right JSF OmniFaces 2 utilities methods/functions

Search on blog

Petition by Java EE Guardians

Twitter

duminică, 15 noiembrie 2015

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&amp;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&amp;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&amp;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&amp;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&amp;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&amp;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&amp;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&amp;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&amp;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

JSF BOOKS COLLECTION

Postări populare

OmniFaces/JSF Fans

Follow by Email

Visitors Starting 4 September 2015

Locations of Site Visitors