Required fields & validation logic.

Nov 18, 2011 at 11:13 AM

Geert,

I want your opinion/thoughts on this:

I have a form with a few fields. Some of he fields are "required" and/or have to be validated. Example: "Email address" is required + should be in the correct format. If I consider both of these as "validation", the validate method on my viewmodel check both that the email address is not blank and that is is in the correct fomat. As a consequence, when the user goes to the form and all fields are empty, he already sees an error indicator on the email address field. From a usabilty point of view, I have issues with this.

I think it would be nicer to have 2 separate notions: "Required" and "(Other) validation" (not including "required" validation). The former would be indicated with an * decorator on the field, the latter would be indicated with an error indicator on the field. The logic to enable/disable the "submit" button should take into account both "Required" and "Other validations".

Do you think that adding this to Catel would be feasable? I think it would be a nice feature. Or do you have other suggestions to achieve this (e.g. making my own viewmodel descandant)?

Geert.

Coordinator
Nov 18, 2011 at 11:23 AM

The fact that a field is required is something that should be done by the UI if you don't want it to be handled by Catel. However, we are aware that there are 2 philosophies: 1 that wants to show the errors upfront, and one that doesn't. Therefore, we have added the property ValidateModelsOnInitialization.

By default, Catel validates the models on initialization. By setting this property to false, you can disable the validation and the validation will only occur when you set values. I think that is exactly what you are looking for.

Nov 18, 2011 at 12:28 PM

I set the ValidateModelsOnInitialization to false on my ViewModel but the ValidateFields method still gets called as soon as the Model is set. I put a break in ValidateFields, and ValidateModelsOnInitialization is still false there.

Am I missing something (Still using Catel2.2 though) ?

Nov 18, 2011 at 12:30 PM

Forgot to mention that my validation is in the ViewModel.

Coordinator
Nov 18, 2011 at 1:57 PM

You are right, the properties in Catel are validated immediately. I have created a task:

http://catel.codeplex.com/workitem/7031

I hope I can make it into 2.4 (should be possible).

Nov 18, 2011 at 2:08 PM

Thanks! Looking forward to that.

Coordinator
Nov 18, 2011 at 4:23 PM

I am trying to reproduce the issue, but if the value is not set at first (so the initial value is used), the error dpes not work, right?

Coordinator
Nov 18, 2011 at 5:29 PM

Ok, I think this feature is implemented, you can try the latest beta build:

http://dl.dropbox.com/u/8455721/Catel%20beta.zip

Dec 9, 2011 at 11:09 AM

I have a ViewModel, and in its constructor, I create my Model instance (otherwise it remains null and nested properties like MyModel.Address remain null). The UI is for a user to register himself with the application.

I have decorated some of my ViewModel properties with [Required] data annotations. With the above feature I would expect the error indicators of the required fields to only appear after the user has entered something in these fields and made them empty again. However, the error indicators appear immediately.

Is there a better way/spot to create the Model instance to have the "delayed error indicators" feature working?

Coordinator
Dec 10, 2011 at 4:15 PM

I think you are looking for the ValidateModelsOnInitialization property.

Dec 12, 2011 at 10:17 AM

No, because the [Required] is on ViewModel properties...

Coordinator
Dec 12, 2011 at 6:49 PM

In that case, you will have to handle the logic yourself. There is no way to set a property and skip the attribute validation for that property (wouldn't be right, would it?).

What you *can* do is create a private field called _isSavedAtLeastOnce, and in the OnValidated check whether _isSavedAtLeastOnce is true. If not, remove the errors again. It is a bit tedious, but that's the way it is.

Dec 12, 2011 at 10:00 PM

I am still confused on this one, see the description of http://catel.codeplex.com/workitem/7031. Can you elaborate on the feature you added there? You sent my a short video that showed the feature working. Maybe you can send me the sample from the video?

Coordinator
Dec 13, 2011 at 7:47 AM

That demo shows 3 types:

1) Validation in view model using attributes

2) Validation in model using attributes (with the ViewModelToModel attribute and ValidateModelsOnInitialization to false)

3) Custom validation with a boolean to check whether validation should occur.

The issue is that when you create the view model, the properties are initialized (so no errors are shown, even when the required attributes are used). However, as soon as you change the model, it starts mapping all the properties from the model to the view model. This means that the properties are actually changed and the validation process kicks in.

I may have a solution, give me some time to check that.

Coordinator
Dec 13, 2011 at 4:56 PM

Ok, I have managed to implement what you want. Now, the ViewModelToModel attribute keeps in mind the ValidateModelsOnInitialization property. If it is set to false, it will not validate the properties when the model is updated.

You can download the latest beta here:

http://dl.dropbox.com/u/8455721/Catel%20beta.zip

Dec 13, 2011 at 7:09 PM

Thanks! I have already downloaded that beta and tried it. Nice work. I knew you could do it :-)

Dec 22, 2011 at 9:22 AM

Again coming back on this one...

- This works great for validations that are done through annotations on the ViewModel properties (like [Required]).
- Validations that are done in e.g. an override of ValidateFields still appear immediately (even with ValidateModelsOnInitialization = false)

Would it be possible to have the same behavior no matter how the validation is implemented?

I am looking into using FluentValidation for all validations (including 'required' fields.

It would be nice if I e.g. could use its email validator and have the following behavior of the UI:
User starts UI with empty email field -> No validation errors shown ->user enters non-valid email address -> user tabs away from that field -> validation error shown. 

Coordinator
Dec 22, 2011 at 9:51 AM

It's hard to do this. What you actually want is to not validate until the user has tried to perform action x first. So, then you should create a private field (bool) that contains a value indicating whether the action is already invoked once. For example, _hasTriedToSaveOnce. If true, validate, otherwise do not validate.

I can implement that it happens on the save, but if you have, for example, another button that would cause another validation sequence to be invoked, it is hard to keep this implemented in a generic way.

I will create an option DeferValidationUntilFirstSave that is false by default. Then you can defer any validation until the view model SaveViewModel method is called at least once (whether it succeeds isn't important). What do you think about this idea?

Dec 22, 2011 at 12:18 PM

With the [Required] annotation, there is already a similar behavior: When UI shows up with the empty field, no validation indicator is shown. When the user starts typing and then deletes all characters again, the validation error shows immediately (maybe the [Required] or annotation based validations follow another path?)

You have a better view on the internals (and the feasibility of implementing my request) so your suggestion would work for me.

Coordinator
Dec 22, 2011 at 4:26 PM

Ok, the property is implemented. I have currently only a boolean that keeps track of whether the SaveViewModel is invoked. If I am in a really good mood, I might implement something like this:

ValidationDeferMode { None, PropertyChange, FirstCallToSaveViewModel }

When using none, validation will always occur. On PropertyChange, the validation for a specific field kicks in when the property first changes. However, what to do with business rules? And finally, the FirstCallToViewModel, which will work just like the property I have introduced (DeferValidationUntilFirstSaveCall).

Tonight, after some more work, I will release a new beta version via NuGet.

Dec 24, 2011 at 11:05 AM

Thanks!

I will try it out next week.

Jan 4, 2012 at 8:24 AM

Tried it out, and it works.

However... when not using [Required] but using e.g. code in ValidateFields, it still does not match 100 % the behavior of the [Required] annotations. E.g. if I start typing in a field marked with [Required] and then delete all characters, it gets flagged with an error. If I do the same for a field that is tested in ValidateFields, this does not happen.

But to achieve that, you needed to be in a good mood (see ValidationDeferMode)... were you/are you?

Coordinator
Jan 6, 2012 at 1:48 PM
Edited Jan 6, 2012 at 1:48 PM

I won't be in such a good mood before the 2.5 release. Maybe for the 2.6, it requires the isdirty check per property. I must think about this carefully.

Please create a task that explains what you want, then we can put it on the roadmap.

Jan 6, 2012 at 3:57 PM

How come it does work for data-annotations then... ? Is it because these validations are done on a per-field level and validations through e.g. ValidateFields methods are done for the complete model or view model? Is so, could it be as "simple" as passing the property name to a ValidateField method?

I will create a task and try to describe as clearly as possible what I want.

Coordinator
Jan 9, 2012 at 5:34 AM

Probably because the .net validator internally checks whether the property value has changed, thus also having an IsDirty per property. We still need to be able to set the errors, we just need to not show them in the summary for this behavior. So, it's the output (or representation system) that must be changed, not the input.