Call f: viewAction conditionally based on f: viewParam

We have a parameter in which we have various optional view parameters passed to JSF pages and subsequent view actions to be processed after the parameters are set. A very simple example is shown below:

page.xhtml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://xmlns.jcp.org/jsf/html"
  xmlns:f="http://xmlns.jcp.org/jsf/core">

<f:view>
    <f:metadata>
        <f:viewParam name="a" value="#{page.a}"/>
        <f:viewAction action="#{page.populateA()}" if="#{not empty page.a}"/>

        <f:viewParam name="b" value="#{page.b}"/>
        <f:viewAction action="#{page.populateB()}"/>
    </f:metadata>

    <h:outputLabel value="#{page.message}"/>
</f:view>
</html>

      

Page

import javax.faces.view.ViewScoped;
import javax.inject.Named;

@ViewScoped
@Named
public class Page {

    private String a;

    private String b;

    private String message;

    public String getA() {
        return a;
    }

    public void setA(String a) {
        this.a = a;
    }

    public String getB() {
        return b;
    }

    public void setB(String b) {
        this.b = b;
    }

    public String getMessage() {
        return message;
    }

    public void populateA() {
        this.message = "Param a given: " + this.a;
    }

    public void populateB() {
        if (this.b != null) {
            this.message = "Param b given: " + this.b;
        }
    }
}

      

Now the difference is that the processing a

doesn't work (page.xhtml? A = 123) while the processing b

works like a charm (page.xhtml? B = 123) - even though I thought I was just translating the null validation from Java to JSF. As far as readability goes, I would prefer to eliminate the extra null checks in Java and completely handle the handling of view parameters in JSF, but how can I customize the code to generate the first script?

EDIT As per the accepted answer What is the purpose of the rendered attribute for f: viewAction? , if

works on its own so I suspect the execution order is wrong (evaluate the action condition first, then apply the values ​​to the model).

+3


source to share


1 answer


An attribute <f:viewAction if>

is basically a renamed attribute <h:xxx rendered>

(I'll leave in the middle whether it becomes clearer or not, it has at least a bug in the autogenerated documentation , namely rendered

instead if

) and thus exactly the same lifecycle, including the Apply phase Request Values, like all other UI components. This phase calls UIComponent#decode()

all of the UI components in the view. For UIViewAction

, the UI component class behind the tag <f:viewAction>

is decode()

described as follows:

Do not take any action if one of the following conditions is true:

So yes, you are indeed correct about the "wrong order of execution". During the request phase, the request is requesting an attribute if

, and if it evaluates false

, then no decoding will be performed and the action event will not be queued. See also line 644 UIViewAction

source code where it tests isRendered()

insice decode()

and fails if false

. In your case #{page.a}

, which you are checking in <f:viewAction if>

, is only available in the Update Model Values ​​phase, which is after the Query Query Values ​​phase.

You want to test for something else that is guaranteed to be available during the request phase of the request. The best candidate would be the actual query parameter itself. All "simple" query parameters are in the EL scope, accessible under the implicit EL object #{param}

that is referenced Map<String, String>

with the parameter name as the key.

So, in general, this should do:

<f:viewParam name="a" value="#{page.a}"/>
<f:viewAction action="#{page.populateA}" if="#{not empty param.a}"/>

      




Update to resolve unintuitive behavior <f:viewAction if>

, the JSF OmniFaces utilities from version 2.2 suggest <o:viewAction>

whose if

only evaluates during the Invoke Application phase, just before the event is broadcast. This trick was done by simply extending the component UIViewAction

and overriding it broadcast()

and isRendered()

as shown below:

@Override
public void broadcast(FacesEvent event) throws AbortProcessingException {
    if (super.isRendered()) {
        super.broadcast(event);
    }
}

@Override
public boolean isRendered() {
    return !isImmediate() || super.isRendered();
}

      

This allows for a more intuitive use of the tag:

<f:viewParam name="a" value="#{page.a}"/>
<o:viewAction action="#{page.populateA}" if="#{not empty page.a}"/>

      

+6


source







All Articles