I'm having an issue with converters and validators.
I have an input text which takes a CSV list. I have a converter that turns it into a list of String. This all works fine. Although I want to make the field required. But with the converter it seems to ignore any validator I attach as well as the required attribute on the input.
I attempted to solve this by throwing a converter Exception if the value is blank. This almost works, although it gets more complicated since I have a radio group just above that on the form with immediate=true. Immediate skips the validator just fine although seems to still attempt the converter. The next best thing I can think of is to validate in my action and add the faces message manually but I'd rather avoid that since I'll have to hard code the client ID into a Java class.
Any idea how to do this properly?
The converter is invoked before the validators.
Inside the converter, you just need to return null when the submitted value is null or an empty string.
#Override
public String getAsObject(FacesContext context, UIComponent component, Object value) {
if (value == null || ((String) value).isEmpty()) {
return null;
}
// ...
}
You should not throw a converter exception when the value is null or empty. This way the validators won't be fired. The converter should after all not validate the value, it should only convert the value.
Related
I have the following as annotation for the custom converter
#FacesConverter(value="capsConverter", forClass=String.class)
public class CapsConverter implements Converter{
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return value.toUpperCase();
}
#Override
public String getAsString(FacesContext context, UIComponent component,Object value) {
if(value!=null && value instanceof String){
String s = (String)value;
s = WordUtils.capitalize(s.toLowerCase());
return s;
}
return null;
}
}
Issue is, the converter sometimes gets called even when I didnt explicitly call it in my page, does the forClass have anything to do with the interference for my inputTexts, should I be using value only and remove forClass=String.class ?
Any shed of light is highly appreciated.
It are actually two distinct ways of registering a converter. Both are mutually exclusive. They have no influence on each other and you can omit one or the other.
The forClass attribute will register the converter in the JSF application to kick in during processing and rendering of every model value of the specified type. In other words, with forClass=String.class, the converter will be implicitly invoked for every String typed input and output, unless the associated input/output component has already an explicit converter declared.
Generally you use forClass only for custom/complex types, not for standard types such as String, Long, BigDecimal, etc for which JSF has already builtin implicit converters.
So you should indeed remove it and rely on converter ID only.
#FacesConverter("capsConverter")
<h:someComponent ... converter="capsConverter" />
An example of correct appliance of forClass would be the following converter between a Person entity and a String representing its ID
#FacesConverter(forClass=Person.class)
on something like
<f:viewParam name="id" value="#{bean.person}" />
which converts an incoming request parameter representing an entity ID like below
/edit-person.xhtml?id=42
to a fullworthy entity property in bean (without needing any additional logic in bean)
private Person person;
In my application, I have the following Validator to validate a captcha input:
#Named(value = "simpleCaptchaValidator")
#RequestScoped
public class SimpleCaptchaValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
Captcha secretCaptcha = (Captcha) session.getAttribute(Captcha.NAME);
// Clear the input field
EditableValueHolder input = (EditableValueHolder) component;
input.resetValue();
// Display an error msg if the entered words are wrong
if (!secretCaptcha.isCorrect(value.toString())) {
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Failed!", "You have entered wrong words.");
throw new ValidatorException(msg);
}
}
}
The above Validator works great on non-Ajax requests when users do enter wrong captcha words. However, if users enter the right captcha words but there's a validation failed on other components, the input field for the captcha is not cleared.
I'd be very grateful if you could show me how to solve the above problem.
It doesn't work because the EditableValueHolder#resetValue() is invoked at the wrong moment. You should be invoking it during invoke application phase (at least, after update model values phase), not in middle of validations phase. Namely, when the validate() method returns without exception, then JSF will still set the validated value as component's local value. When the validations phase has failed in general, then the update model values phase won't be invoked and the local value still sticks around in the component.
Based on the information provided so far, it isn't possible to propose the right approach. If this is a custom component, then just don't write the value during encode(). Or, if this is an existing component, then perhaps your best bet is to create a custom converter which stores the submitted value as a custom component attribute and then returns null in getAsObject(). In the validator, you then just grab that custom component attribute as value. As last resort, attach a PhaseListener which resets the component at the right moment.
i have some input components that should be validated only when a specific action is executed under all other circumstances they should accept every input.
This way i can't use a normal validator but have a commandButton that evaluates the data in it's action Method and creates some FacesMessages related to specific clientIds if something is missing.
Now i normaly use the OmniFaces o:highlight component to point to fields that require further action but in this case the input-components are valid and thus the highlight component does not take them into account.
Now i wonder if it would be possible to have this behavior depended on the List of Ids with Messages.
Something like this:
for (Iterator<String> it = FacesContext.getCurrentInstance()
.getClientIdsWithMessages(); it.hasNext();) {
String clientId = it.next();
List<FacesMessage> messageList = FacesContext
.getCurrentInstance().getMessageList(clientId);
if (messageList != null) {
for (FacesMessage msg : messageList) {
... // build json with clientIds (maybe check for UIInput
}
}
}
If needed this way one could possibly introduce new Style classes for info, warn and error messages. Maybe it's even a bit faster cause not the whole component tree has to be visited, but that s just a guess.
So what s your opinion? This is a rather hard change on the current behavior so i m not sure if this guess will make it into omnifaces or must be implemented individualy.
Now i wonder if it would be possible to have this behavior depended on the List of Ids with Messages.
From the javadoc of the very same method as you linked there:
Note that the FacesContext#getClientIdsWithMessages() could also be consulted, but it does not indicate whether the components associated with those client IDs are actually UIInput components which are not UIInput#isValid().
So, you would for every single client ID still need to use UIViewRoot#findComponent() in order to find the component, figure its type and verify the validity. This is much more expensive than a single tree visit.
If you really need to perform validation in action method, your best bet is to mark the context and inputs as invalid yourself.
FacesContext context = FacesContext.getCurrentInstance();
context.validationFailed();
((UIInput) context.getViewRoot().findComponent(clientId)).setValid(false);
Alternatively, to satisfy the concrete functional requirement,
i have some input components that should be validated only when a specific action is executed under all other circumstances they should accept every input.
just use a normal validator wherein you check the invoked action:
public void validate(FacesContext context, UIComponent component, Object value) {
if (!context.getExternalContext().getRequestParameterMap().containsKey("formId:buttonId")) {
return;
}
// ...
}
I.e. when <h:form id="formId"><h:commandButton id="buttonId"> is invoked, then validation will be performed. That's always better than performing validation at the wrong place.
I may be out of date, but one principle I adhere to is avoid nulls as much as possible.
However what I have found is that for a strongly typed view in which the user inputs the properties of an object I want to save, if some fields are not entered they are assigned as null.
Then when you try to save the changes, the validation fails.
So rather than set each property to an empty string, how can I automatically set each TextBox on a form to default to an empty string rather than a null?
You could put the following attribute on your string-properties in your model:
[DisplayFormat(ConvertEmptyStringToNull=false)]
So whenever someone posts a form with empty text-fields, these will be an empty string instead of null...
To be honest, I'd say your coding methodology is out of date and flawed. You should handle all possibilities, it's not hard. That's exactly what string.IsNullOrEmpty(value); is for.
I'm guessing your validation logic is something like:
if (value == string.Empty) { isValid = false; }
So it doesn't handle the null values. You should replace that check so it also checks for nulls.
string value1 = null;
string value2 = string.Empty;
string.IsNullOrEmpty(value1); // true
string.IsNullOrEmpty(value2); // true
An alternative solution to using attributes on each model property, as described in the accepted answer, is using a custom model binder, see string.empty converted to null when passing JSON object to MVC Controller
I ran across this problem when dealing with an old service that requires empty strings. I created an extension method:
public static string GetValueOrDefault(this string str)
{
return str ?? String.Empty;
}
So you can use this when you want to make sure any strings that are null become empty
yourString1.GetValueOrDefault();
yourString2.GetValueOrDefault();
My Sql Server database has some nullable nvarchar fields, and no nvarchar fields containing empty strings. I want to keep it this way, but the default MVC model binder seems to turn null strings into empty strings.
When a controller retrieves a null nvarchar database field, the null field turns into null string inside the controller, and from there the view renders them, say as blank text boxes. When the page is posted, the default model binder uses these blank text boxes to update the model, and the formerly null strings are changed to empty strings. When the data is updated back to the database, nulls are overwritten with empty strings.
What is the easiest way to get model binding to leave these nulls unchanged?
I know you are probably looking for something more sophisticated, but the default behavior of the ModelBinder is to convert empty form field values into the default value for the datatype of your model object property. String properties become empty, int properties become 0, etc.
You can obviously create a validation scheme that will check for string.empty and convert to null prior to updating the DB. For int form fields you will need to check for 0, and then convert to null.
Here's a hack I used a few months ago before I found the eden of stackoverflow. :) It's a pain, and doesn't scale well, but it works:
Basically, you override the binding inside of a partial linq object. If there's a value you know should always be null (but never legitimately empty) you can do the following. I used this for a string-based user id (SID).
partial void OnSubProcess_Owner_UserChanged()
{
if (string.IsNullOrEmpty(this.SubProcess_Owner_User))
this._SubProcess_Owner_User = null;
}
James
The right answer might be to override the default Model binder to add this functionality yourself.
Maybe you could have a NullValueAttribute that you could apply to string properties to identify the null value. Then make empty string a null value.
I am experiencing the same problem at the moment and will probably resort to this
I'm posting this answer to follow through on this question. After working with it for a while I came to see this problem as part of the general concern of model integrity. For a while I had implemented a solution inside my update stored procedures to catch empty strings and turn them to nulls, along the lines of mikerennick's answer above. Later I wanted also to make sure fields were trimmed and I happened to move the application to NHibernate (and most of the stored procedures went away). In the end I embedded some POCO logic to trim and check for empty strings (from whatever source) in the setters as so:
public MyClass {
private string _name;
public string Name {
get { return _name; }
set { _name = value.TrimToNullIfEmpty(); }
}
}
public static class StringExtensions {
public static string TrimToNullIfEmpty(this string s) {
string temp = (s ?? "").Trim();
return temp.Length == 0 ? null : temp;
}
}