eWorld.UI - Matt Hawley

Ramblings of Matt

ASP.NET MVC: UI Validation Framework Update

May 14, 2008 08:53 by matthaw

I've taken some recommendations regarding the MVC UI Validation framework I originally posted about, which is available within the MvcContrib project. I'm still working on future advancements like attributing your model, but I did make a significant step forward ensuring both client and server side validation is successful. With some simple changes to how you are generating your validators, you can easily achieve this new functionality. Let's use an example:

   1:  using MvcContrib.UI.Tags.Validators;
   2:   
   3:  public class UserController : Controller
   4:  {
   5:     public ActionResult Index()
   6:     {
   7:        return RenderView();
   8:     }
   9:   
  10:     public ActionResult Add()
  11:     {
  12:        return RenderView(new UserData());
  13:     }
  14:   
  15:     public ActionResult Save()
  16:     {
  17:        UserData data = new UserData();
  18:        BindingHelperExtensions.UpdateFrom(data, Request.Form);
  19:   
  20:        if (!BaseValidator.Validate(Request.Form, data.Validators))
  21:           return RenderView("Add", data); // You should follow PRG pattern
  22:   
  23:        // save the data
  24:   
  25:        return RedirectToAction("Index");
  26:     }
  27:  }
  28:   
  29:  public class ValidatorViewData
  30:  {
  31:     private IList<IValidator> validators = new List<IValidator>();
  32:   
  33:     public IList<IValidator> Validators
  34:     {
  35:        get { return validators; }
  36:     }
  37:  }
  38:   
  39:  public class UserData : ValidatorViewData
  40:  {
  41:     public UserData()
  42:     {
  43:        this.Validators.Add(new RequiredValidator("firstNameRequired", "firstName", 
  44:                "First Name is Required"));
  45:        this.Validators.Add(new RegularExpressionValidator("firstNameRegex", "firstName", 
  46:                "[a-zA-Z]*", "First Name can only contain letters."));
  47:        this.Validators.Add(new RequiredValidator("ageRequired", "age", "Age is required."));
  48:        this.Validators.Add(new RegularExpressionValidator("ageRegex", "age", "[0-9]*", 
  49:                "Age can only be numeric."));
  50:        this.Validators.Add(new RangeValidator("ageRange", "age", "18", "35", 
  51:                ValidationDataType.Integer, "Age is not in the range of 18-35."));
  52:     }
  53:   
  54:     public string FirstName { get; set; }
  55:     public string Age { get; set; }
  56:  }

In this controller and view data, we see that we're generating our validators within the view data object. This enables us to validate the Request.Form collection against our list of validators as seen in line 20 above. This works because when we created each of our validators, we gave it the name of the form element we're expecting the value to be present in. When taking it to our view implementation, we'll use a new method to register our validators:

   1:  <%@ Import Namespace="System.Web.Mvc" %>
   2:  <%@ Import Namespace="MvcContrib.UI.Html" %>
   3:  <html>
   4:     <head>
   5:        <%= Html.Validation().ValidatorRegistrationScripts() %>
   6:     </head>
   7:     <body>
   8:        <% using (Html.Form<UserController>(c => c.Save(), Formmethods.Post, 
   9:              Html.Validation().FormValidation())) { %>
  10:        First Name: <%= Html.TextBox("firstName", this.ViewData.FirstName) %>
  11:        <%= Html.Validation().ElementValidation(this.ViewData.Validators, "firstName");
  12:        <br />
  13:        Age: <%= Html.TextBox("age", this.ViewData.Age) %>
  14:        <%= Html.Validation().ElementValidation(this.ViewData.Validators, "age");
  15:        <br />
  16:        <%= Html.SubmitButton("submit", "Save") %>
  17:        <% } %>
  18:     </body>
  19:  </html>

What you'll notice from the original is that we now have an ElementValidation extension method. This enables you to specify the list of validators you would like to be rendered at this time. You have the option of specifying the field name to filter the list by when rendering as well. Should any validators fail upon the server, and you re-render the view, those validators will initially be displayed. All in all, the approach is getting closer to what it needs to be. I'll be working further on the following features to really bring this to be a first class citizen:

  1. Move the Validate method from BaseValidator into a Controller extension method.
  2. Define a set of attributes that you can apply to your model's properties that will work with this validation model.

A few things to note, is that you can still explicitly use individual validators, but note that they're no longer off of Html.Form() but Html.Validation() now. Lastly, while these changes have been committed to the MvcContrib trunk, they're still not available in any publicly released builds. You'll have to download the source and compile the binaries yourself until MvcContrib does another official build. Let me know what you think, and yes - I know, some people still don't like this approach :) It's not meant for everyone, and there's other projects out there that you can use. Enjoy, and let me know what you think of the progress.

kick it on DotNetKicks.com



Comments

May 20. 2008 12:56

Pingback from weblogs.asp.net

ASP.NET MVC: UI Validation Framework Update - eWorld.UI - Matt Hawley

weblogs.asp.net

May 20. 2008 16:34

Matt, I continue following your experiences and ideas on MVC.Net (please keep them coming!) and I remain a non-believer. How can you look at that markup and claim it a good thing? Its barely readable by a developer. Its completely opaque to a designer, and also to any web design tool.

Michael Teper

May 20. 2008 22:19

Hi,
I'm using a similar approach to validation and I'm looking for a solution to a problem it creates. From your code it looks like the URL will remain '/save' if validation has failed and the user is returned to the add view, ultimately the URL should read '/add' when you are returned to that view. AFAIK the only way to do this is use RedirectToAction to redirect to the Add action. This doesn't really work as you have to resend all the form inputs to the Add action so that it can maintain state, causing it to use UpdateFrom again. you also have no real way to pass the validation errors across without a bit of hacking.

What is the best practise for this scenario?

Harry

May 20. 2008 23:44

Yes, my example is trivial. Check out my post on PRG and that's exactly how I would approach this if it were not for it being just an example. Going forward, all of my examples will use the PRG pattern (which is what you mentioned).

matthaw

May 21. 2008 04:10

Pingback from blog.cwa.me.uk

Reflective Perspective - Chris Alcock  » 2008 » May » 20

blog.cwa.me.uk

May 21. 2008 13:22

Pretty good stuff.  I'd say +1 for adding attributes to your model and then using a class that can read those rather than subclassing some viewdata base class.

Keep this stuff coming Matt Smile

@Michael -- I generally don't go helper-happy and create most of my pages with pure HTML, but there are some cases where ANY web platform breaks down... particularly when dealing with routes or with dynamic values intermingled with strings (though Brails & NVelocity don't look as ugly as webforms in this regard) -- so my views look like XHTML compliant code that any designer would understand.

I'd rather be in control of my markup than deal with webforms abstractions from it (which are sometimes flawed).

Ben Scheirman

June 17. 2008 00:13

Hi Matt,

I have taken a similar approach to doing mvc validation, which I is detailed here

xsation.blogspot.com/.../...dation-server-and.html

I have created a custom Form object to deal with the posting, the form has fields and validations. I didn't use POCO for the fields because if you are expecting a int and it doesn't parse, then there isn't a way to persist this value back to the user if you define it as an  int..

I look forward to seeing where this goes and what comes out of ms for validation..

Mark Kemper

August 14. 2008 15:29

Is this available in the MvcContrib?  More specifically ViewData.Validators is where I'm seenig issues...what's the latest on this?

Tim Barcz

March 21. 2009 08:12

Very nice solution

Bayram Çelik

June 4. 2009 02:41

You may also want to check out the Validator Toolkit ASP.NET MVC on Codeplex. That helped me.
http://mvcvalidatortoolkit.codeplex.com

tom

Comments are closed

Copyright © 2000 - 2024 , Excentrics World