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:
- Move the Validate method from BaseValidator into a Controller extension method.
- 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.
c3b8660b-1930-425f-9308-8d42bd3998f4|2|1.0