What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?

jsf, jsf-2, viewaction, viewparams

Solution

Process GET parameters

The `<f:viewParam>` manages the setting, conversion and validation of GET parameters. It's like the `<h:inputText>`, but then for GET parameters.

The following example

<f:metadata>
    <f:viewParam name="id" value="#{bean.id}" />
</f:metadata>

does basically the following:

- Get the request parameter value by name `id`.

- Convert and validate it if necessary (you can use `required`, `validator` and `converter` attributes and nest a `<f:converter>` and `<f:validator>` in it like as with `<h:inputText>`)

- If conversion and validation succeeds, then set it as a bean property represented by `#{bean.id}` value, or if the `value` attribute is absent, then set it as request attribtue on name `id` so that it's available by `#{id}` in the view.

So when you open the page as `foo.xhtml?id=10` then the parameter value `10` get set in the bean this way, right before the view is rendered.

As to validation, the following example sets the param to `required="true"` and allows only values between 10 and 20. Any validation failure will result in a message being displayed.

<f:metadata>
    <f:viewParam id="id" name="id" value="#{bean.id}" required="true">
        <f:validateLongRange minimum="10" maximum="20" />
    </f:viewParam>
</f:metadata>
<h:message for="id" />

Performing business action on GET parameters

You can use the `<f:viewAction>` for this.

<f:metadata>
    <f:viewParam id="id" name="id" value="#{bean.id}" required="true">
        <f:validateLongRange minimum="10" maximum="20" />
    </f:viewParam>
    <f:viewAction action="#{bean.onload}" />
</f:metadata>
<h:message for="id" />

with

public void onload() {
    // ...
}

The `<f:viewAction>` is however new since JSF 2.2 (the `<f:viewParam>` already exists since JSF 2.0). If you can't upgrade, then your best bet is using `<f:event>` instead.

<f:event type="preRenderView" listener="#{bean.onload}" />

This is however invoked on every request. You need to explicitly check if the request isn't a postback:

public void onload() {
    if (!FacesContext.getCurrentInstance().isPostback()) {
        // ...
    }
}

When you would like to skip "Conversion/Validation failed" cases as well, then do as follows:

public void onload() {
    FacesContext facesContext = FacesContext.getCurrentInstance();
    if (!facesContext.isPostback() && !facesContext.isValidationFailed()) {
        // ...
    }
}

Using `<f:event>` this way is in essence a workaround/hack, that's exactly why the `<f:viewAction>` was introduced in JSF 2.2.

Pass view parameters to next view

You can "pass-through" the view parameters in navigation links by setting `includeViewParams` attribute to `true` or by adding `includeViewParams=true` request parameter.

<h:link outcome="next" includeViewParams="true">
<!-- Or -->
<h:link outcome="next?includeViewParams=true">

which generates with the above `<f:metadata>` example basically the following link

<a href="next.xhtml?id=10">

with the original parameter value.

This approach only requires that `next.xhtml` has also a `<f:viewParam>` on the very same parameter, otherwise it won't be passed through.

Use GET forms in JSF

The `<f:viewParam>` can also be used in combination with "plain HTML" GET forms.

<f:metadata>
    <f:viewParam id="query" name="query" value="#{bean.query}" />
    <f:viewAction action="#{bean.search}" />
</f:metadata>
...
<form>
    <label for="query">Query</label>
    <input type="text" name="query" value="#{empty bean.query ? param.query : bean.query}" />
    <input type="submit" value="Search" />
    <h:message for="query" />
</form>
...
<h:dataTable value="#{bean.results}" var="result" rendered="#{not empty bean.results}">
     ...
</h:dataTable>

With basically this `@RequestScoped` bean:

private String query;
private List<Result> results;

public void search() {
    results = service.search(query);
}

Note that the `<h:message>` is for the `<f:viewParam>`, not the plain HTML `<input type="text">`! Also note that the input value displays `#{param.query}` when `#{bean.query}` is empty, because the submitted value would otherwise not show up at all when there's a validation or conversion error. Please note that this construct is invalid for JSF input components (it is doing that "under the covers" already).

See also:

- ViewParam vs @ManagedProperty(value = "#{param.id}")

- Communication in JSF 2.0 - Processing GET request parameters

Problem

Can anyone clarify how we can use in general, or a in real world example, this snippet? ``` <f:metadata> <f:viewParam id="id" value="#{bean.id}" /> <f:viewAction action="#{bean.init}" /> </f:metadata> ```

Original source

Related problems