LA.NET [EN]

Dec 17

Today we’re going to look at the validation support introduced by the framework. Do keep in mind that we’re not talking about using validators like the one you have in the ASP.NET web forms world. Nop! here validators are only responsible for showing an error message to the user. In practice, this means that you have to signal that a control has an incorrect value and the MVC framework will pick that info and show the appropriate error message where you’ve  dropped the “validator”.

The best way to understand this is to give a simple example. Let’s reuse yesterday’s form:

<form method="post" action="">
  <%= Html.TextBox( "name”) %>
  <input type="submit" id="myBt" value="Try to submit" />
</form>

We’ll also assume that we can only accept non-empty names with more than 4 characters on the name textbox. When that doesn’t happen, we will want to add a message saying that. In order to show a message next to the control, we can simply call the ValidationMessage method where we’d like to show the error message:

<form method="post" action="">
  <%= Html.TextBox( "name", "hallo", new { @class = "myInput"} ) %>
  <%= Html.ValidationMessage("name")%>
  <input type="submit" id="myBt" value="Try to submit" />
</form>

Notice that the ValidationMessage extension method will only be responsible for showing a specific error message. We still need to define that message and signal when there’s an error with the value passed to the control. As I’ve said, we’re only accepting non-null names with more than 4 characters. To signal that there’s something wrong with the value of a control we will need to add an error message to the  ModelState instance associated with that control. The simplest way to do this is to write the following code on the controller’s method that is responsible for handling the request:

var name = Request.Form["name"];
if( String.IsNullOrEmpty(name) || name.Length < 4 ) {
   ViewData.ModelState.AddModelError("name", 
                                                 "name must have more than 4 chars.");
}

If we run the page and write “la”, then this is what we’ll see:

validation1

The AddModelError method will end up creating a ModelState instance and it will associate it to an existing control named “name” (that is, if there isn’t any associated with the control identified by the first parameter) . Note that there’s also an overloaded version of this method which receives an exception.

There’s an interesting gotcha here which I must mention. The ViewDataDictionary.ModelState property presented in the previous snippet isn’t really of type ModelState. The fact is that it’s a property of type ModelStateDictionary which  is a dictionary of pairs string/ModelState (where the string identifies the control’s name on the page).

Now is probably a good time to look at the ModelState class. It’s really a simple class with only two properties: Attempted Value and Errors (i’m only showing its public API).

public class ModelState {
    public string AttemptedValue {
        get;
        set;
    }
    public ModelErrorCollection Errors {
        get {
            return _errors;
        }
    }
}

As you have probably guessed by now, calling the AddModelError method results in the instantiation of a new ModelError object which is added the to ModelState instance. The ModelError class is really simple and it has only two properties: Exception and ErrorMessage. One thing I didn’t mention is that you can add several error messages by calling AddModelError several times.

The AttemptedValue property of the ModelState class isn’t set up when you call the AddModelError method. This property is important and you can use it to pass a value that will be shown by the control. For instance, when you look at the previous figure,you’ll notice that the textbox is empty when there’s an error with the input. A better option would be to show the wrong value entered by the user. Here’s how you could do that:

var name = Request.Form["name"];
if( String.IsNullOrEmpty(name) || name.Length < 4 ) {
   var state = new ModelState() {
                    AttemptedValue = name
   };
   state.Errors.Add("name must have more than 4 chars.");
   ViewData.ModelState.Add("name",state);
}

If you load the page and pass it an incorrect value, you’ll notice that you’ll get the error message and that the textbox won’t loose the last introduced value.

Even though you can pass several error messages to a control, the truth is that with the current API, you’ll only see one error per validator associated to a specific control. You can easily change this behaviour (I’ll probably show an easy way in the next post) if you require.

Before ending this post, there’s still time to present the ValidationSummary method extension. As you’ve probably guessed, this method will show you all the error messages associated with the controls shown by the current view. It will generate an unordered list with all the errors maintained by all the ModelState instances that have been registered with the ViewDataDictionary instance.

When you use a summary, you probably will not want to repeat the error message in front  of the control. In those cases, you could probably identify the control that has an error with a special symbol (ex.: *) and just put the error message on the summary. To achieve this, you’ll need to specify the error message presented by the validator by calling one of the overloads of the ValidationMessage method that receives a validation message. Changing the view so that it looks like the following snippet is all that it takes to get this behaviour:

<form method="post" action="">
     <%= Html.TextBox( "name" ) %>
     <%= Html.ValidationMessage("name","*")%>
     <input type="submit" id="myBt" value="Try to submit" /> <br />
     <%= Html.ValidationSummary() %>
</form>

Finally, there’s still time to talk about the options you have to customize the style of the errors presente d by the “validator” and the validation summary. The easiest way to customize them is to use the overloads that receive the attributes rendered by the control. Both methods have two overloads that will let you specify the values of the attributes: one uses the anonymous object approach; the other ends up using a dictionary. By default, the framework ends up using some predefined css style classes. You can always create your own too and then pass that info through one of the overload methods. Here’s a quick example:

<%= Html.ValidationMessage("name","*", new { @class="test"})%>

In this case, the predefined css class will be applied as an alternative css class, which means that the “test” css class will be applied to the span that holds the validation message. Notice that the textbox will still have a red border and in the current release I’m not sure that you’ll be able to change the applied css easily (I think there’s a bug on the code, but I’ll confirm it before talking about it).

And that’s all for today. Keep tuned!

1 comment so far

  1. adafanfkankanvkmaavaaa
    8:29 pm - 5-28-2010

    afavvava

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>