views:

1171

answers:

2

MVC isn't generating the client-side validation rules for my viewmodel. The HTML just contains this:

<script type="text/javascript">
//<![CDATA[
if (!window.mvcClientValidationMetadata) { window.mvcClientValidationMetadata = []; }
window.mvcClientValidationMetadata.push({"Fields":[],"FormId":"form0","ReplaceValidationSummary":false});
//]]>
</script>

Note that Fields[] is empty!

My view is strongly-typed and uses the new strongly-typed HTML helpers (TextBoxFor(), etc).

View Model / Domain Model

public class ItemFormViewModel
{
    public Item Item { get; set; }
    [Required] [StringLength(100)] public string Whatever { get; set; } // for demo
}
[MetadataType(typeof(ItemMetadata))]
public class Item
{
    public string Name { get; set; }
    public string SKU { get; set; }
    public int QuantityRequired { get; set; }
    // etc.
}
public class ItemMetadata
{
    [Required] [StringLength(100)] public string Name { get; set; }
    [Required] [StringLength(50)] public string SKU { get; set; }
    [Range(0, Int32.MaxValue)] public int QuantityRequired { get; set; }
    // etc.
}

(I know I'm using a domain model as my / as part of my view model, which isn't a good practice, but disregard that for now.)

View

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage<ItemFormViewModel>" %>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Editing item: <%= Html.Encode(Model.Item.Name) %></h2>

    <% Html.EnableClientValidation(); %>
    <%= Html.ValidationSummary("Could not save the item.") %>

    <% using (Html.BeginForm()) { %>

        <%= Html.TextBoxFor(model => model.Item.Name) %>
        <%= Html.TextBoxFor(model => model.Item.SKU) %>
        <%= Html.TextBoxFor(model => model.Item.QuantityRequired) %>

        <%= Html.HiddenFor(model => model.Item.ItemID) %>

        <%= Html.TextBox("Whatever", Model.Whatever) %>
    <input type="submit" value="Save" />
    <% } %>
</asp:Content>

I included the Whatever property on the view model because I suspected that MVC wasn't recursively inspecting the sub-properties of ItemFormViewModel.Item, but even that isn't being validated? I've even tried delving into the MVC framework source code but have come up empty. What could be going on?

A: 

I have had no luck getting this to work in MVC 2 RC. According to other questions here on SO, you have to get the MicrosoftMvcJQueryValidation.js file from the MVC Futures release, hold your left foot behind your head, and whistle Dixie for half an hour. I did this and more and have not been able to make it work.

Hopefully it will be fixed in RTM.

Dave Swersky
I have the necessary JS referenced. The issue is that the MVC framework isn't emitting the JSON that defines the validation rules the client should apply to the form. I'm using the RTM release.
Brant Bobby
+4  A: 

About five seconds after I posted the question, I realized something: My view didn't have ValidationMessage placeholders anywhere. I added <%= Html.ValidationMessageFor(model => model.Item.Name) %> and lo and behold, MVC added validation rules for Item.Name to the JS block at the bottom of the page.

It turns out that MVC does not emit client-side validation rules for a field unless you actually do one of the following:

  1. Call Html.ValidationMessage() for the property.
  2. Call Html.Validate() for the property. (This one won't output error messages)
  3. Render the controls using Html.EditorForModel(). (source)

Doing any of these tells MVC, "This property of my viewmodel is editable by the user, so you should be validating it." Just using the HTML helper to stick a textbox on the page -- even if you're using the new strongly-typed helpers -- isn't enough.

Brant Bobby
Had my answer all typed out when your's appeared. EnableClientValidation simply sets up the ViewContext for client-side validation by enabling a FormContext. Fields are only added to the FormContext when you use one of the methods that displays or validates a particular field. This gives you the flexibility of doing client or server-side validation at the same time on different fields. That wouldn't be the case if the field were registered for validation by simply including it on the form.
tvanfosson
The key part is also to register your validator. I was scratching my head when I didn't do it :) Global.asax :: Application_Start..Add this statement.DataAnnotationsModelValidatorProvider .RegisterAdapter(typeof(ItemFormatAttribute), typeof(ItemFormatValidator));
cdpnet