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

2

159

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>
Buchholz answered 16/6, 2011 at 19:56 Comment(0)
S
299

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:

Shuttering answered 16/6, 2011 at 20:8 Comment(14)
@Shuttering What should be the scope of "bean" be when used in conjunction with faces-redirect=true ? Will it work as expected if the scope is set to "@RequestScoped" ?Comber
@Geek: A redirect creates a new GET request. The bean scope of the source and target bean is irrelevant. You should however take the possible implications of a new GET request into account for a request and view scoped bean. See also stackoverflow.com/questions/7031885/…Shuttering
@Shuttering What exactly you mean by "You should however take the possible implications of a new GET request into account for a request and view scoped bean."Comber
@Geek: They will be trashed and recreated because their scope will then end and start.Shuttering
@BalusC. A comprehensive answer. "When you need to use a '@'PostConstruct like feature for view scoped beans which isn't invoked on every request, check if the request isn't a postback". If it isn't invoked on every request then why to check if the request is a postback or not?Wiener
@UlukBiy: where do you read that the <f:event type="preRenderView"> is not invoked on every request? It is invoked on every request.Shuttering
I quoted from your answer above in my previous comment. I meant, if '@'PostConstruct was put on init() method mentioned in answer then still we needed to check facesContext.isPostback()?Wiener
@UlukBiy: I think you mixed up them both. The @PostConstruct of a view scoped bean is not invoked on every request.Shuttering
Note that there was a namespace change for <f:viewAction> and this only applies to a recent version of JSF, etc. See: stackoverflow.com/questions/6377798/…Aplasia
@Shuttering regarding that part "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.", are you talking about using the c:set tag? (I can't see the exact meaning, I guess I'm confused with the #{param.id} )Reckon
@Shuttering For JSF 2.1, I used <f:event type="preRenderView" listener="#{bean.onload}" /> to access view param in onload after removing @PostContruct from onload. I have a dropdown on the page (non-ajax since I intend to submit change on submit). On page submit (redirect), the changed value of that dropdown resets to default (first item), I can see dropdown setter being called twice. First for the change made by user & second for its auto-reset as I said. When I go back to @PostConstruct again, things become normal. Setter is called only once for user last choice. What can I do here?Bertsche
This section is for comments on the answer not for spinoff/offtopic questions. For that, press the "Ask Question" button right top.Shuttering
@Shuttering I am sorry, I have asked this as a new question here stackoverflow.com/questions/36402368/…Bertsche
@BalusC: The view action has navigation capabilities too, while the preRenderView event listener doesn't. While the view action can naturally accomplish navigation tasks( return a view id from the action method & a redirection will take place), the preRenderView event listener requires explicit navigation based on the JSF API(I mean making use of ConfigurableNavigationHandler). You must be knowing it very well. I mean you can include that too your answer.Patricide
F
2

Send params from View to an other View, from Sender View to Receiver View use viewParam and includeViewParams=true

In Sender

  1. Declare params to be sent. We can send String, Object,…

Sender.xhtml

<f:metadata>
      <f:viewParam name="ID" value="#{senderMB._strID}" />
</f:metadata>
  1. We’re going send param ID, it will be included with “includeViewParams=true” in return String of click button event Click button fire senderMB.clickBtnDetail(dto) with dto from senderMB._arrData

Sender.xhtml

<p:dataTable rowIndexVar="index" id="dataTale"value="#{senderMB._arrData}" var="dto">
      <p:commandButton action="#{senderMB.clickBtnDetail(dto)}" value="見る" 
      ajax="false"/>
</p:dataTable>

In senderMB.clickBtnDetail(dto) we assign _strID with argument we got from button event (dto), here this is Sender_DTO and assign to senderMB._strID

Sender_MB.java
    public String clickBtnDetail(sender_DTO sender_dto) {
        this._strID = sender_dto.getStrID();
        return "Receiver?faces-redirect=true&includeViewParams=true";
    }

The link when clicked will become http://localhost:8080/my_project/view/Receiver.xhtml?*ID=12345*

In Recever

  1. Get viewParam Receiver.xhtml In Receiver we declare f:viewParam to get param from get request (receive), the name of param of receiver must be the same with sender (page)

Receiver.xhtml

<f:metadata><f:viewParam name="ID" value="#{receiver_MB._strID}"/></f:metadata>

It will get param ID from sender View and assign to receiver_MB._strID

  1. Use viewParam In Receiver, we want to use this param in sql query before the page render, so that we use preRenderView event. We are not going to use constructor because constructor will be invoked before viewParam is received So that we add

Receiver.xhtml

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

into f:metadata tag

Receiver.xhtml

<f:metadata>
<f:viewParam name="ID" value="#{receiver_MB._strID}" />
<f:event listener="#{receiver_MB.preRenderView}"
            type="preRenderView" />
</f:metadata>

Now we want to use this param in our read database method, it is available to use

Receiver_MB.java
public void preRenderView(ComponentSystemEvent event) throws Exception {
        if (FacesContext.getCurrentInstance().isPostback()) {
            return;
        }
        readFromDatabase();
    }
private void readFromDatabase() {
//use _strID to read and set property   
}
Foreman answered 12/9, 2020 at 15:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.