I have a lot of code with standard required validation.
Something like this
<h:inputText required="true" requiredMessage="it is required!"/>
Now, I need to change behavior of RequiredValidator: put error message to the context, but do not interrupt cycle. May be to do something more.
I tried to add custom validator with the same id, but it did not work.
<validator>
<validator-id>javax.faces.Required</validator-id>
<validator-class>my.RequiredValidator</validator-class>
</validator>
Is it possible?
Empty fields are not committed to validators. I could not find a matching documentation, but I tested the environment and please take a look at Get empty strings from <h:inputText> go through validation.
This question also handles an empty string input and BalusC says:
JSF 1.x does by default not fire validators on empty fields.
OK, it's about JSF 1.X, but it seems it didn't change.
Validators are to interrupt the request, but that's not what you want. So could it be a workaround to use the setter method for your needs?
Ommit the required attribute from your inputText and add a FacesMessage in the setter for your textInput. Assumed the assingned value is #{myBean.description}, the setter may look like this:
public void setDescription(String value) {
String _tmp = value.trim();
if (_tmp.equals("")) {
FacesContext ctx = FacesContext.getCurrentInstance();
ctx.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR
, "Error:", "Input must not be empty"));
this.description = null;
return;
}
this.description = _tmp;
}
Related
I'm using Spring Validation within a Spring MVC application that delegates validation to Hibernate Validator 5. I'm successfully able to have beans validated and have the messages interpolated by the validator. However, it's important that I also be able to have access to the message template itself, pre-interpolation.
For example, in some bean I have validation #Size(min=5,max=15,message="{my.custom.message}". In a messages.properties file I have entry my.custom.message=test min {min} and max {max}. In my BindingResult, I see the ObjectError with error message "test min 5 and max 15", but I need to look a value up at this point based on the non-interpolated my.custom.message raw value.
Can this be done? If it can't out of the box, can someone point me in the right direction for how I might customize spring's LocalValidatorFactoryBean to preserve this?
Update
I'm looking at extending org.springframework.validation.beanvalidation.SpringValidatorAdapter, and wrapping the getArgumentsForConstraint to automatically append the pre-interpolated message to the returned list of arguments. The notion of exactly what these 'arguments' are and how they're used is unclear to me, but if it's purely used for message interpolation, it seems relatively safe for me to append at the end. Any reason this might not work? Problems it might cause? Better ideas?
Solution
Didn't find any great solutions other than my 'update' above, so I ended up subclassing LocalValidatorFactoryBean with this:
#Override
protected Object[] getArgumentsForConstraint(String objectName, String field, ConstraintDescriptor<?> descriptor) {
if (null == descriptor) return super.getArgumentsForConstraint(objectName, field, descriptor);
Object[] orig = super.getArgumentsForConstraint(objectName, field, descriptor);
if (null == orig || orig.length < 1) return new Object[] { descriptor };
Object[] retval = new Object[orig.length+1];
System.arraycopy(orig, 0, retval, 0, orig.length);
retval[retval.length-1] = descriptor;
return retval;
}
In subsequent code, I look at the last object in this array and test to see if it's an instance of ConstraintDescriptor. Good enough I suppose.
However, it's important that I also be able to have access to the message template itself, pre-interpolation.
In which context do you need to access the template? If it is after validation, then getMessageTemplate() on ConstraintViolation gives you this. If it is within a constraint validator implementation, then you could use getDefaultConstraintMessageTemplate() on ConstraintValidatorContext.
I'm writing a validator method in JSF 2. I have a ui:repeat element in my page that renders a list of items. Each item has a date property, and I need to ensure the dates correspond to each other in a specific sequence, e.g. the date of the last item in the list doesn't come before the date of the first item. I was trying to get all the child elements inside the ui:repeat and iterate over them to do the comparison, but I don't really know where to start. I've seen how to get a specific element by ID:
UIInput input = (UIInput) context.getViewRoot().findComponent(elementId);
However, within the ui:repeat the ID values are made unique by JSF, so I don't know what they are at compile time. Furthermore, at compile time I don't know how many items there will be in the list.
I've looked at the Javadoc for UIViewRoot and other associated classes, and have tried a couple things, but I'm getting errors, things aren't working, and I don't really know if I'm even close to getting anywhere. I'm leaving the code of my attempts out of this post, becuase they're probably a joke.
There's physically only one UIInput component whose state changes depending on the current iteration round of UIRepeat. It's available by just its client ID without the UIRepeat index: findComponent("formId:inputId") (the UIRepeat index is only of significance in the client side). However, when the component is programmatically been accessed outside the context of UIRepeat this way, then it'll indeed return a seemingly empty state.
In order to visit the UIInput component in all those states as they are inside the UIRepeat and collect their values, you need to run UIComponent#visitTree() on the UIRepeat.
Here's a kickoff example:
<ui:repeat value="#{bean.items}" var="item">
<f:event type="postValidate" listener="#{bean.validateOrder}" />
<h:inputText value="#{item.value}" />
</ui:repeat>
With this validateOrder() method (again, just a kickoff example, this approach naively assumes that there's only one UIInput component in the repeater):
#SuppressWarnings("rawtypes")
public void validateOrder(ComponentSystemEvent event) {
final FacesContext context = FacesContext.getCurrentInstance();
final List<Comparable> values = new ArrayList<Comparable>();
event.getComponent().visitTree(VisitContext.createVisitContext(context), new VisitCallback() {
#Override
public VisitResult visit(VisitContext context, UIComponent target) {
if (target instanceof UIInput) {
values.add((Comparable) ((UIInput) target).getValue());
}
return VisitResult.ACCEPT;
}
});
boolean ordered = new ArrayList<Comparable>(new TreeSet<Comparable>(values)).equals(values);
if (!ordered) {
event.getComponent().visitTree(VisitContext.createVisitContext(context), new VisitCallback() {
#Override
public VisitResult visit(VisitContext context, UIComponent target) {
if (target instanceof UIInput) {
((UIInput) target).setValid(false);
}
return VisitResult.ACCEPT;
}
});
context.validationFailed();
context.addMessage(null, new FacesMessage("Values are not in order!"));
}
}
Note that it visits the tree twice; first time to collect the values and second time to mark those inputs invalid. Also note that this very specific requirement can't be done with a standard JSF validator. You can't attach a <f:validator> on <ui:repeat>. Attaching it on <h:inputText> is theoretically possible, but it would cause the very same validator to run as many times as the amount of repeated items, which doens't make sense. Also, the validator would need to take getSubmittedValue() vs getValue() into account this way.
OmniFaces has an <o:validateOrder> component which does a similar thing on fixed components, but it isn't designed for usage in dynamically repeated components.
I am looking for a possibility to write programmatically ajax call on every element.
I have to ways, to build the UI Components
first - panelGroup binding- )
HtmlSelectOneMenu HSOM = new HtmlSelectOneMenu();
UISelectItems items = new UISelectItems();
List<SelectItem> comboList = new ArrayList<SelectItem>();
comboList.add(new SelectItem(" "));
comboList.add(new SelectItem("1"));
comboList.add(new SelectItem("2"));
comboList.add(new SelectItem("3"));
items.setValue(comboList);
HSOM.getChildren().add(items);
HSOM.setValueExpression("value", buildValueExpression("#{productDetails.productOptionValue}"));
AjaxBehavior ajax = new AjaxBehavior();
ajax.setValueExpression("value", buildValueExpression("#{productDetails.updateProduct()}"));
HSOM.addClientBehavior("valueChange", ajax);
HSOM.addValidator(new BeanValidator());
productOptions.getChildren().add(HSOM);
private ValueExpression buildValueExpression(String exp) {
FacesContext facesInstance = FacesContext.getCurrentInstance();
Application application = facesInstance.getApplication();
ExpressionFactory expressionFactory = application.getExpressionFactory();
String expression = exp;
return expressionFactory.createValueExpression(facesInstance.getELContext(), expression, String.class);
}
I can see, that a Ajax Call is linked to the component, but the updateProduct() function did not get called.
the other possibility to create the dynamic components is)
public void encodeEnd(FacesContext context) throws IOException {
System.out.println("Start encoding");
ResponseWriter responseWriter = context.getResponseWriter();
responseWriter.startElement("span", null);
responseWriter.writeAttribute("id",getClientId(context),"id");
responseWriter.writeAttribute("name", getClientId(context),"clientId");
responseWriter.write("Farbe");
responseWriter.endElement("span");
responseWriter.startElement("select", null);
responseWriter.writeAttribute("id",getClientId(context),"id");
responseWriter.writeAttribute("name", getClientId(context),"clientId");
responseWriter.writeAttribute("value", "#{artikelDetails.productOptionValue}", "value");
responseWriter.startElement("option", null);
responseWriter.write("Gelb");
responseWriter.endElement("option");
responseWriter.startElement("option", null);
responseWriter.write("Blau");
responseWriter.endElement("option");
responseWriter.endElement("select");
System.out.println("End encoding");
}
How to add a ajax call on every select ele here ?
And which of both method's do you prefer ?
This is a very simple example, where i do not build lot of select ele via loop
first i need to get this work...
You need to give all programmatically created input and command components a fixed ID, so that JSF can find the desired submitted information in the request parameter map. Otherwise they end up getting an autogenerated ID which is different during postback.
In your case, that's thus:
HSOM.setId("someId");
And which of both method's do you prefer ?
None of both. I'm confident that Java is the wrong tool for the purpose of declaring components in the view. JSF already ships with Facelets out the box which allows declaring components in a much easier and cleaner way by XML means. If you intend to build the view dynamically based on some preconditions, look at JSTL. See also among others How to make a grid of JSF composite component? and JSTL in JSF2 Facelets... makes sense?
I am busy writing my own JSF2 UIComponents and their relevant renderers. All of my UIComponents implements ClientBehaviorHolder. What I don't understand is how to really render ClientBehaviorHolder.
For example, the following code illustrates how ClientBehaviorHolder is rendered in Mojarra.
private static void renderHandler(FacesContext context,
UIComponent component,
Collection<ClientBehaviorContext.Parameter> params,
String handlerName,
Object handlerValue,
String behaviorEventName,
String submitTarget,
boolean needsSubmit,
boolean includeExec)
throws IOException {
ResponseWriter writer = context.getResponseWriter();
String userHandler = getNonEmptyUserHandler(handlerValue);
List<ClientBehavior> behaviors = getClientBehaviors(component, behaviorEventName);
// Don't render behavior scripts if component is disabled
if ((null != behaviors) &&
(behaviors.size() > 0) &&
Util.componentIsDisabled(component)) {
behaviors = null;
}
if (params == null) {
params = Collections.emptyList();
}
String handler = null;
switch (getHandlerType(behaviors, params, userHandler, needsSubmit, includeExec)) {
case USER_HANDLER_ONLY:
handler = userHandler;
break;
case SINGLE_BEHAVIOR_ONLY:
handler = getSingleBehaviorHandler(context,
component,
behaviors.get(0),
params,
behaviorEventName,
submitTarget,
needsSubmit);
break;
case SUBMIT_ONLY:
handler = getSubmitHandler(context,
component,
params,
submitTarget,
true);
break;
case CHAIN:
handler = getChainedHandler(context,
component,
behaviors,
params,
behaviorEventName,
userHandler,
submitTarget,
needsSubmit);
break;
default:
assert(false);
}
writer.writeAttribute(handlerName, handler, null);
}
For submit handlers, Mojarra adds the mojarra.jsfcljs javascript, UIParameters and other scripts. For chain handlers, jsf.util.chain is used.
My question is:
How does one determine if we have to render handlers in chain or a single behaviour or user specific handler?
mojarra.jsfcljs is only unique to Mojarra. PrimeFaces have their own implementation, so does Apache Tomahawk. Question is: what does mojarra.jsfcljs do and what is its use? This is so that I can write one for my own? Also, where can I find the implementation of mojarra.jsfcljs?
What is the specification to render ClientBehaviorHolder?
My sincere thanks in advance.
How does one determine if we have to render handlers in chain or a single behaviour or user specific handler?
Imagine that the enduser (read: the JSF developer who's using your component) programmed:
<your:component onclick="return foo()" />
And you intented to ultimately render for your component's own purpose:
<someHtmlElement onclick="jsf.ajax.request(...); return false;" />
Then you can't just concatenate the enduser's onclick in front of your component's jsf.ajax.request() like so
<someHtmlElement onclick="return foo(); jsf.ajax.request(...); return false;" />
Even if it returned true, your component's jsf.ajax.request won't be invoked at all. You ultimately want to end up something like:
<someHtmlElement onclick="if returnsTrue('return foo();') { jsf.ajax.request(...); } return false;" />
That's exactly what jsf.util.chain() is doing under the covers.
mojarra.jsfcljs is only unique to Mojarra. PrimeFaces have their own implementation, so does Apache Tomahawk. Question is: what does mojarra.jsfcljs do and what is its use? This is so that I can write one for my own? Also, where can I find the implementation of mojarra.jsfcljs?
It's inside the jsf.js file. Easy way to find it is to open a JSF page with <f:ajax> embedded and look in the generated <head> source for the <script> with its URL. This file is by default minified. If you set javax.faces.PROJECT_STAGE context param to Development, then this will be served unminified. The task of the jsfcljs() function is to submit the parent form with the necessary parameters. Here's an extract of relevance coming from Mojarra 2.1.21.
/*
* This is called by command link and command button. It provides
* the form it is nested in, the parameters that need to be
* added and finally, the target of the action. This function
* will delete any parameters added <em>after</em> the form
* has been submitted to handle DOM caching issues.
*
* #param f - the target form
* #param pvp - associative array of parameter
* key/value pairs to be added to the form as hidden input
* fields.
* #param t - the target of the form submission
*/
mojarra.jsfcljs = function jsfcljs(f, pvp, t) {
What is the specification to render ClientBehaviorHolder?
Use ClientBehavior#getScript() to get the autogenerated script. It requires a ClientBehaviorContext argument which can be created using ClientBehaviorContext#createClientBehaviorContext(). It's in turn your responsibility to render it into the appropriate HTML attribute, such as onclick.
FacesContext context = FacesContext.getCurrentInstance();
UIComponent inputOrCommandComponent = ...; // Your component.
String event = "click"; // Just the particular HTML DOM event name you need to listen on.
ClientBehaviorContext clientBehaviorContext = ClientBehaviorContext.createClientBehaviorContext(context, component, event, component.getClientId(context), null);
StringBuilder builder = new StringBuilder();
for (ClientBehavior behavior : component.getClientBehaviors().get(event)) { // Collect all <f:ajax> declarations on the given event.
builder.append(behavior.getScript(clientBehaviorContext));
builder.append(';');
}
String script = builder.toString();
// Write it to the desired HTML attribute.
Note that you absolutely don't need to worry about writing JSF implementation specific scripts this way. They will be generated for you.
All with all, ClientBehaviorHolder is just an abstraction of ajax support. It allows developers to nest <f:ajax> in your component. All standard JSF UIInput and UICommand components implement it.
I have the following input field:
<h:inputText value=".." validator="#{labController.validateValue}"/>
If the field is empty it will not be validated (validateValue in labController is not called).
But using a separate validator class:
<h:inputText value=".." >
<f:validator validatorId="labDateValidator"/>
</h:inputText>
then its validate method well be called even with an empty input field?
This is what I observe. Is this behavior dependent on implementation or version (I am using Mojarra 2.1) ?
Background is that I want to use my own method/class for all validation tasks including required validation. Does it work with validator class only?
You've hit an oversight in JSF. The validator is wrapped in a MethodExpressionValidator which according to the source of its validate() method indeed skips validation when the value is null.
if (value != null) {
try {
ELContext elContext = context.getELContext();
methodExpression.invoke(elContext, new Object[]{context, component, value});
...
This is not considering the new JSF 2.0 javax.faces.VALIDATE_EMPTY_FIELDS context parameter which defaults to true. It should actually be doing the same as UIInput#validate().
if (!isEmpty(newValue) || validateEmptyFields(context)) {
try {
ELContext elContext = context.getELContext();
methodExpression.invoke(elContext, new Object[]{context, component, value});
...
This has already been reported as Mojarra issue 1508. I've voted it and added a new comment to wake them up.