Override default validator of input in composite component - validation

I'm developing a composite component with a default validator that may be used alongside a more restrictive custom validator. However, it didn't work as intented.
I boiled down the problem as below:
<h:inputText id="textId" value="#{bean.text}">
<f:validateLength for="textId" maximum="10" />
<f:validateLength for="textId" maximum="5" />
</h:inputText>
The validation for maximum=5 is not executed or the result is omitted, as only the maximum=10 rule results in proper feedback:
Text field: 1234567890123
searchForm:textId: Validation Error: Length is greater than allowable maximum of '10'
Text field: 1234567
passes
What is the reason behind this behavior? How can I achieve my requirement anyway?

from your comment, I assume, you have a component like this:
<cc:interface >
<cc:editableValueHolder name="text" targets="myText" />
</cc:interface>
<cc:implementation>
<h:inputText id="myText">
<f:validateLength maximum="10" />
</h:inputText>
</cc:implementation>
And you want to use it - assuming its called myInput - like this:
<ns:myInput>
<f:validateLength maximum="5" for="text" />
</ns:myInput>
hence, if no validateLength is given, your 10 should apply, else the other value, if it's more restrictive.
I think it is by design, that you can only use ONE validator for a certain validation type. So, to overcome this issue you have 3 options:
First, you could use a regex validator inside the component, like:
<h:inputText id="myText">
<f:validateRegex pattern=".{,10}" />
</h:inputText>
This would allow the component user to apply his own f:validateLength. But then ofc. he would no longer be able to apply his own f:validateRegex.
Another Option would be, to make the component user able to disable the build-in validation with a component attribute. But this however would completly bypass your contraint of length 10 if the user decides to disable it.
The best option (imho) would be to apply the maxLength as an own attribute, and validate that option against your contraint of max. 10 digits:
<cc:interface >
<cc:attribute name="maxLength" required="false" default="10"/>
</cc:interface>
<cc:implementation>
<h:inputText id="myText">
<f:validateLength maximum="#{(cc.attrs.maxLength > 10)? 10 : cc.attrs.maxLength}" />
</h:inputText>
</cc:implementation>
Usage would now be:
<ns:myInput maxLength="15">
<!-- This will apply your contraint of 10 -->
</ns:myInput>
<ns:myInput maxLength="3">
<!-- This will apply the component users constraint of 3 -->
</ns:myInput>
This also has the advantage, that the component user does not need to know about the actual name of the encapsuled editableValueHolder in order to apply a f:validateLength.
Also think about, if it really makes sence to limit the maxLength inside your component. Components should be able to work for any use-case on the designed datatype, unless they control the appearence of the component, for instance column count, items per page and the like.

Related

Mutually exclusive input fields with JSF and PrimeFaces

I have a PrimeFaces application where I would like to make two input text fields mutually exclusive: the user should fill either field, but not both.
In the following example, the user could search contacts either by phone number or by email.
<h:form>
<h:outputLabel>Phone Number:
<h:inputText id="phoneInput" value="#{contactSearch.phone}"
disabled="#{not empty contactSearch.email}">
<p:ajax event="keyup" update="emailInput"/>
</h:inputText>
</h:outputLabel>
<h:outputLabel>Email Address:
<h:inputText id="emailInput" value="#{contactSearch.email}"
disabled="#{not empty contactSearch.phone}">
<p:ajax event="keyup" update="phoneInput"/>
</h:inputText>
</h:outputLabel>
<!-- Possibly other fields... -->
<h:commandButton value="Search" action="#{contactSearch.search}"/>
</h:form>
Is this the proper way to do it? I am concerned about:
Possible "deadlock" issues, where both fields end up being disabled
The whole form being submitted and/or valdiated in the Ajax request, when I just would like to update the disabled state of the input field
Missing some predefined PrimeFaces control which does all the work :)
This is not trivial in standard JSF. JSF utility library OmniFaces has a validator for exactly this purpose, the <o:validateOne>.
<h:inputText id="email" ... />
<h:inputText id="phone" ... />
<o:validateOne components="email phone" />
However, in UX perspective, you'd better redesign your form to provide a single UISelectOne component to select the type and a single UIInput field to enter the value. E.g.
<h:selectOneRadio ... value="#{bean.type}">
<f:selectItem itemValue="email" />
<f:selectItem itemValue="phone" />
</h:selectOneRadio>
<h:inputText ... value="#{bean.value}" />
Update: as server side validation is not affordable and you'd really like to disable the the other without making much ajax traffic, your only bet is using pure JavaScript/jQuery for the job (if necessary in combination with server side validation as fallback).
Here's a generic jQuery based approach:
<h:inputText styleClass="onlyone" a:data-onlyone="groupName" />
<h:inputText styleClass="onlyone" a:data-onlyone="groupName" />
...
$(document).on("input", ":input.onlyone", function() {
var $this = $(this);
var $others = $("[data-onlyone='" + $this.data("onlyone") + "']").not($this);
$others.val(null).attr("disabled", !!$this.val());
});
The data-onlyone value should represent the group name, so you can have multiple of "only one" inputs throughout the document.

Validate two components with dynamic id

Here is what I am trying to do:
I have a form with multiple pairs of input fields and a save button.
When the save button is pressed then for each pair of input fields should be validated if they are numbers and if the left value is smaller than the right value.
If not the validation errors will be shown and if it is the case a dialog will pop up to ask for the name under which it should be saved.
Here is what I got so far:
<h:form>
<ui:repeat value="#{myBean.someList}" var="elem">
<h:inputText id="min#{elem.id}" value="#{elem.minimum}" />
<h:inputText id="max#{elem.id}" value="#{elem.maximum}" />
<br />
<h:message for="min#{elem.id}" style="color:red" />
<h:message for="max#{elem.id}" style="color:red" />
</ui:repeat>
<h:commandButton value="save">
<f:ajax execute="#form" render="#form updateValidationFailedFlag"
onevent="
function(ajaxResult){
if(ajaxResult.status=='success' && !globalValidationFailedFlag)
showSaveDialog();
}"
/>
</h:commandButton>
<h:outputScript id="updateValidationFailedFlag">
globalValidationFailedFlag = #{facesContext.validationFailed};
</h:outputScript>
</h:form>
This works but doesn't check if the minimum is smaller then the maximum.
It will validate the input (checks if the input are integer) and shows the save dialog if no validation error occured.
To check if the minimum is smaller then the maximum I tried to follow the tutorial at http://www.mkyong.com/jsf2/multi-components-validator-in-jsf-2-0/
Variant 1 adds a listener after the validation is done and is able to add error messages that are shown in the browser. But that doesn't count as a validation failure and doen't set the facesContext.validationFailed flag.
Variant 2 writes a custom validator for one component and gives the other component as parameter to that validator. That would look something like this:
<f:validator validatorId="myValidator" />
<f:attribute name="maximum" value="#{max#{elem.id}}" />
That is not really valid EL and I don't know how to write it correctly.
What can I do to validate if each of those min-max pairs is valid
You don't need #{elem.id} here. The <ui:repeat> already takes care of that. It would be evaluated to an empty string anyway when JSF needs to set the id attribute.
<ui:repeat value="#{myBean.someList}" var="elem">
<h:inputText id="min" value="#{elem.minimum}" />
<h:inputText id="max" value="#{elem.maximum}" />
<br />
<h:message for="min" style="color:red" />
<h:message for="max" style="color:red" />
</ui:repeat>
As to the validation, just pass the value of the other component along:
<h:inputText id="min" binding="#{min}" value="#{elem.minimum}" />
<h:inputText id="max" value="#{elem.maximum}">
<f:validator validatorId="myValidator" />
<f:attribute name="min" value="#{min.value}" />
</h:inputText>
Note that I moved the validator to the second component. Otherwise it would be still invoked if the second component didn't pass conversion.
If you happen to use JSF utility library OmniFaces, then you could use its <o:validateOrder> instead.
<h:inputText id="min" value="#{elem.minimum}" />
<h:inputText id="max" value="#{elem.maximum}" />
<o:validateOrder components="min max" showMessageFor="min" />
See also:
How to use EL with <ui:repeat var> in id attribute of a JSF component
JSF doesn't support cross-field validation, is there a workaround?

Execute some code when a value from a p:selectOneMenu is selected

I am doing a password editor. The password can be entered in two ways: manually entering the values for password and confirmation or by selecting some already generated password.
To use the generated password, a new value from the select box must be chosen. The change triggers the filling of the password/confirmation fields(psw1 and psw2) with the value from the selected value.
<p:selectOneMenu value="#{password.selectedPassword}" >
<f:selectItems value="#{password.passwords}" var="val"
itemLabel="#{val}" itemValue="#{val}" />
<p:ajax update="psw1, psw2" listener="#{password.passwordChanged}"/>
</p:selectOneMenu>
I need to implement the filling of password fields also when the the same value is selected. How can I implement this? One way is to add an extra value, a default empty value.
You can't trigger a change event if the value has not changed. Indeed, one way is to supply a default value as in "Please select" with a #{null} value or even with noSelectionOption="true" in flavor of an additional <f:selectItem>. This forces the enduser to actually change the value to a valid value.
E.g.
<p:selectOneMenu value="#{password.selectedPassword}" >
<f:selectItem itemValue="#{null}" itemLabel="--select--" />
<f:selectItems value="#{password.passwords}" var="val"
itemLabel="#{val}" itemValue="#{val}" />
<p:ajax update="psw1, psw2" listener="#{password.passwordChanged}"/>
</p:selectOneMenu>
Another way is to use <p:selectOneListbox> instead, which is maybe better if you don't have many items.
<p:selectOneListbox value="#{password.selectedPassword}" >
<f:selectItems value="#{password.passwords}" var="val"
itemLabel="#{val}" itemValue="#{val}" />
<p:ajax update="psw1, psw2" listener="#{password.passwordChanged}"/>
</p:selectOneListbox>

How to validate values instantly, but check for required only on full submit?

I am currently adding validation to a form. There are two things to check: The correctness of the value itself (e.g. if it is a positive integer or valid email) and whether all required fields were filled in.
However, if by some means (f:ajax or IceFaces partialSubmit attribute) I make the validation for type correctness happen at once (e.g. when the field loses focus), it will also check the required attribute in this same step. In most cases this is no problem, as the user already entered a value and is likely to correct it rather than go back to a blank field.
However, in the case where he actually wants to clear the field again, he can no longer do so without getting an error. In consequence, I only want to check the required-ness of fields on finally submitting the page.
At the moment, my only idea to separate the two validation types is by performing all required-checks in the backing beans action method, thus tying it to directly to the final submit via button.
Is there another way?
(For background: The reason why one might want to clear the field again is that requirements can change depending on other selections in the form. So one might decide to not provide this field after all and only after that correct the option that actually makes this field optional.)
Just let the required attribute evaluate true when the submit button has really been pressed.
The answer however depends on the way how your submit button executes its logic (standard, f:ajax, ICEfaces, etc). But it basically boils down to that you could check the request parameter map for a request parameter which indicates that the desired submit button has been pressed.
E.g., if it's a standard command button:
<h:form id="form">
...
<h:commandButton id="submit" value="Submit" action="#{bean.submit}" />
</h:form>
Then you could check for it by checking if the button's client ID is present in the request parameter map:
<c:set var="submitButtonPressed" value="#{not empty param['form:submit']}" />
...
<h:inputText ... required="#{submitButtonPressed}" />
<h:inputText ... required="#{submitButtonPressed}" />
<h:inputText ... required="#{submitButtonPressed}" />
Or, if it's a <f:ajax> button:
<h:form id="form">
...
<h:commandButton id="submit" value="Submit" action="#{bean.submit}">
<f:ajax execute="#form" ... />
</h:commandButton>
</h:form>
Then you could check it by checking if javax.faces.source parameter equals the button's client ID:
<c:set var="submitButtonPressed" value="#{param['javax.faces.source'] == 'form:submit'}" />
...
<h:inputText ... required="#{submitButtonPressed}" />
<h:inputText ... required="#{submitButtonPressed}" />
<h:inputText ... required="#{submitButtonPressed}" />
You could even combine both:
<c:set var="submitButtonPressed" value="#{not empty param['form:submit'] or param['javax.faces.source'] == 'form:submit'}" />
...
<h:inputText ... required="#{submitButtonPressed}" />
<h:inputText ... required="#{submitButtonPressed}" />
<h:inputText ... required="#{submitButtonPressed}" />

How to let validation depend on the pressed button?

I have created form and I want to show previous existing items on a table while a new one is creating. I'd like to show matching items as form is filling up. But when I try to filter the list without having the form completed, the validation messages appear and the table doesn't get updated.
Don't know if it's possible, but what I want to do something like this:
<h:form id="form">
<h:outputText value="Name: "/>
<p:inputText value="#{itemsBean.name}" id="name" required="true"/>
<br/>
<h:outputText value="Description: "/>
<p:inputText value="#{itemsBean.description}" id="description" required="true"/>
<p:commandButton value="Save" update="form" actionListener="#{itemsBean.save}"/> //validate and save
<p:commandButton value="Filter" update="form" actionListener="#{itemsBean.updateItemsList}"/> //don't validate, and update the table.
<p:dataTable id="list" value="#{itemsBean.itemsList}" var="item">
<p:column>
<h:outputText value="#{item.name}"/>
</p:column>
<p:column>
<h:outputText value="#{item.description}"/>
</p:column>
</p:dataTable>
</h:form>
I'm very new to JSF.
I understand that you want to filter based on the name input field. The <p:commandButton> sends by default an ajax request and has a process attribute wherein you can specify which components you'd like to process during the submit. In your particular case, you should then process only the name input field and the current button (so that its action will be invoked).
<p:commandButton process="#this name" ... />
The process attribute can take a space separated collection of (relative) client IDs of the components, wherein #this refers to the current component. It defaults in case of <p:commandButton> to #form (which covers all input fields of the current form and the pressed button), that's why they were all been validated in your initial attempt. In the above example, all other input fields won't be processed (and thus also not validated).
If you however intend to skip the required validation for all fields whenever the button in question is been pressed, so that you can eventually process multiple fields which doesn't necessarily need to be all filled in, then you need to make the required="true" a conditional instead which checks if the button is been pressed or not. For example, let it evaluate true only when the save button has been pressed:
<p:inputText ... required="#{not empty param[save.clientId]}" />
...
<p:inputText ... required="#{not empty param[save.clientId]}" />
...
<p:commandButton binding="#{save}" value="Save" ... />
This way it won't be validated as required="true" when a different button is pressed. The trick in the above example is that the name of the pressed button (which is essentially the client ID) is been sent as request parameter and that you could just check its presence in the request parameter map.
See also:
Understanding PrimeFaces process/update and JSF f:ajax execute/render attributes
I Have tested this with non-ajax submits:
<p:inputText ... required="#{not empty param.includeInSave1}" />
...
<p:inputText ... required="true" />
...
<p:commandButton value="Save1" ajax="false">
<f:param name="includeInSave1" value="true" />
</p:commandButton>
<p:commandButton value="Save2" ajax="false" />
The first input is required validated only on Save1 button submit.
Additionally to the BalusC answer (very useful and complete) I want to add that when you use a <h:commandButton /> it will validate (required, custom validations) all the fields in the <h:form /> where the command button is located, therefore when you need to use more than one command button you could consider that it is a good practice to use different <h:form /> to different responsibilities to avoid unexpected behavior in submit actions of the command buttons.
It is well explained in a BalusC answer: Multiple h:form in a JSF Page
If your form has validations and you do not update the <h:form /> or you do not show messages, you could get a headache thinking that the <h:commandButton /> is not firing your action, but likely is a validation problem that has not been shown.
Change your filter commandbutton like this to ignore validation:
<p:commandButton value="Filter" update="list" actionListener="#{itemsBean.updateItemsList}" process="#this"/>
EDIT:
The related post on SO, I think this will solve your issue too
JSF 2.0: How to skip JSR-303 bean validation?

Resources