1

When validation fails, I want to submit the form nevertheless by ignoring the invalid fields in a second submit. The isolated problem is as follows:

My form consists of two required inputs foo and bar, which are additionally validated in a h:hiddenInput and a custom validator named MyValidator, which for example ensures the inequality of the inputs. If MyValidator fails, I instead render another submit button that skips the h:hiddenInput and its validator by processing only the inputs foo and bar.

<h:form>
    <h:panelGroup layout="block" id="fooBarWrapper">
        <p:inputText value="#{myControl.foo}" binding="#{foo}" required="true"/>
        <p:inputText value="#{myControl.bar}" binding="#{bar}" required="true"/>
    </h:panelGroup>

    <h:inputHidden validator="myValidator" binding="#{myValidation}">
        <f:attribute name="foo" value="#{foo}"/>
        <f:attribute name="bar" value="#{bar}"/>
    </h:inputHidden>

    <p:messages/>

    <p:commandButton action="#{myControl.doSomething()}" value="Do something" 
                     process="@form" update="@form" 
                     rendered="#{myValidation.valid}"/>

    <p:commandButton action="#{myControl.doAnotherThing()}" value="Do another thing" 
                     process="fooBarWrapper" update="@form"
                     rendered="#{not myValidation.valid}"
                     oncomplete="if (!args.validationFailed) { console.log('valid'); }"/>
</h:form>

with

@Named
@FacesValidator("myValidator")
public class MyValidator implements Validator {

    @Override
    public void validate(FacesContext ctx, UIComponent component, Object value) throws ValidatorException {

        String foo = (String) ((UIInput) component.getAttributes().get("foo")).getValue();
        String bar = (String) ((UIInput) component.getAttributes().get("bar")).getValue();

        if (foo != null && bar != null && foo.equals(bar)) {
            throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Foo and bar must not be equal", ""));
        }
    }
}

and

@Named
@ViewScoped
public class MyControl implements Serializable {

    private String foo;
    private String bar;

    public void doSomething() {
        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Did something"));
    }

    public void doAnotherThing() {
        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Did another thing"));
    }

    ...
 }

However, the action of the second button is not called, although MyValidator is now skipped as expected. Interestingly, PrimeFaces args.validationFailed indicates that the validation was successful.

Can anyone explain, why #{myControl.doAnotherThing()} is not called, although no validation seems to fail?

stan
  • 95
  • 2
  • 8
  • Did you try setting `immediate="true"` on your second `commandButton`? – Parkash Kumar Aug 29 '17 at 10:36
  • 1
    And what is that `-` doing in your `process` attribute's value i.e. `-#{p:component('fooBarWrapper')}`as you can simply set `process="fooBarWrapper"`. Also, no need to add `()` if you are invoking a parameter less bean method. – Parkash Kumar Aug 29 '17 at 10:38
  • (1.) That `-` is my `javax.faces.SEPARATOR_CHAR`, as I don't need to escape it in any jQuery selectors. However, `process="fooBarWrapper"`is absolutly sufficient here. I updated my question. Thank you! (2.) I cannot see how `immediate="true"` can help me here. As pointed out in https://stackoverflow.com/a/12961162, that would ignore all inputs which do _not_ have `immediate="true"`. But I do want to include them in my form submit. – stan Aug 29 '17 at 14:14

1 Answers1

1

The Problem has nothing to do with the validator of the h:inputHidden at all. You could strip down your example even more.

<h:form>
    <h:panelGroup layout="block" id="fooBarWrapper">
        <p:inputText value="#{myControl.foo}"/>
        <p:inputText value="#{myControl.bar}"/>
    </h:panelGroup>

    <p:messages/>

    <p:commandButton action="#{myControl.doAnotherThing()}" value="Do another thing" process="fooBarWrapper" update="@form"/>
</h:form>

This example will also not work. To make this work you have to process the commandButton too.

<p:commandButton action="#{myControl.doAnotherThing()}" value="Do another thing" process="@this fooBarWrapper" update="@form"/>
Jan Schmitz
  • 1,181
  • 9
  • 13