pickypg
pickypg

Reputation: 22342

Overwrite JSF viewParam with postback, then redirect to itself

I have a JSF 2.0 page that I want to allow a GET parameter to come into, and to have that set a bean's value. Additionally, I want to allow that bean to be modified from a form on the same page, and then redirect back to the same page with the new GET parameter in place. As you probably expect, I want this so that the page can be bookmarked.

However, I am hitting problems when the original GET request contains an invalid (fails validation) parameter for the bean being re-validated. The problem was not obvious to me at first, but upon checking the log it became very obvious what was happening. When the commandButton checks the validity of the freshly typed value, JSF is validating the original GET parameter as well as the newly typed value. As the GET parameter failed validation in the first place, it fails again. Unfortunately, that prevents the new value from taking its place.

Even when the GET values are valid, they are still checked again, which is a waste of time, before validating the new values. Is there a way to basically throw away the GET parameters once they are loaded, whether or not they pass validation, so that only the current values are validated against?

Details:

I include a search form on the main page, and it does Ajax validation (standard bean validation using h:inputText and p:commandButton (Primefaces), but this also fails when I use h:commandButton) to ensure that the user submits data (not blank). Upon successful validation, the page is redirected using the action:

listing?faces-redirect=true&includeViewParams=true

The form:

<h:form id="search">
    <p:messages id="qError" />
    <h:inputText value="#{pageSearchBean.search}" id="q"
                 validator="#{pageSearchBean.validateSearchNotBlank}" />
    <p:commandButton value="Find" action="#{pageSearchBean.searchAction}"
                     update="qError" />
</h:form>

(Note: The issue still happens even if I remove the validator from the h:inputText as long as I leave it on the viewParam)

This part works perfectly. Upon success, the page redirects to listing.jsf?q=xyz as expected, and plugs in the passed value ( into the pageSearchBean, then it does the search based on the preRenderView event. If I enter an invalid value on the homepage, then the button properly informs me that the data was invalid; after correcting it, then it redirects properly to listing.jsf?q=whatever+I+typed.

The translation from GET parameter to bean:

<f:metadata>
    <f:viewParam name="q" value="#{pageSearchBean.search}"
                 validator="#{pageSearchBean.validateSearchNotBlank}" />
    <f:event type="preRenderView" listener="#{pageSearchBean.doSearch}" />
</f:metadata>

Now, on the listing page, I reuse that exact same search form/Facelet by importing the same Facelet, but I also show results for the actual search analogous to pretty much every search page you have ever used.

As-is, the page always does a double check

public class PageSearchBean implements Serializable
{
    protected static final Log LOGGER =
            LogFactory.getLog(PageSearchBean.class);

    protected final List<Page> results = new ArrayList<Page>();
    protected String search = "";

    public String getSearch()
    {
        // all logging just to watch how things are requested
        LOGGER.fatal(String.format("getSearch() = '%s'", search));

        return search;
    }

    public void setSearch(String search)
    {
        LOGGER.fatal(String.format("setSearch('%s')", search));

        this.search = search;
    }

    public List<Page> getResults()
    {
        return results;
    }

    public void doSearch()
    {
        LOGGER.fatal(String.format("doSearch() for '%s'", getSearch()));

        // generate random data/search results (if we haven't already
        //  and we have search terms)
    }

    public String searchAction()
    {
        LOGGER.fatal("searchAction()");

        return "listing?faces-redirect=true&amp;includeViewParams=true";
    }

    public void validateSearchNotBlank(FacesContext context,
                                       UIComponent validate,
                                       Object value)
            throws ValidatorException
    {
        LOGGER.fatal(String.format("validate('%s')", value));

        // throws new ValidatorException when blank (null or all whitespace)
    }
}

Upvotes: 0

Views: 1349

Answers (1)

pickypg
pickypg

Reputation: 22342

One unattractive solution is to avoid validation on the GET parameters at the JSF level, and simply validate the values at the setter level (ignore invalid data there).

As I am willing to accept unset parameters for my use case, this is an okay workaround here. However, I am hoping for a more useful solution that allows me to avoid cluttering the setter with validation because the JSF page is unable to not-double validating.

Upvotes: 1

Related Questions