1

I want to create a page to add, edit and delete a collection of objects. I want to do the add/edit/delete operations with javascript. Right now, I have the following HTML:

<table class="table table-striped">
    <thead>
        <tr>
            <td>Name</td>
            <td>Number</td>
            <td>Country</td>
            <td></td>
        </tr>
    </thead>
    <tbody>
    @foreach(var n in Model)
    {
        <tr>
            <td class="name">@n.Name</td>
            <td class="value">@n.Value</td>
            <td class="country-name">@n.CountryName</td>
            <td><ahref="#">Edit</a> | <a href="#">Delete</a></td>
        </tr>
    }
    </tbody>
</table>

<div class="modal">
    <form>
        <label>Name</label>
        <input type="text" />
        <label>Value</label>
        <input type="text />
        <button type="submit">Save</button>
    </form>
</div>

If the user presses 'Edit', javascript code populates the modal div with the relevant data and displays it on the screen. When the user presses 'Submit', the data is submited with ajax, the modal dialog is hidden, and the table is updated with the new data.

I want to validate the form before it is submitted. If possible, I would like to make use of the unobtrusive client validation system, so that changes to the data annotations of my model class are automatically reflected in the clientside code. In a more standard example, I could use the @Html.EditorFor() methods to generate the correct HTML, but this isn't possible here.

Unobstrusive validation is controlled using attributes on the html elements, like this:

<input data-val="true" data-val-required="This field is required." id="Name"
    name="Name" type="text">
<span class="field-validation-valid" data-valmsg-for="Name"
     data-valmsg-replace="true"></span>

Is it possible to generate the relevant HTML for a particular model class without referencing a particular instance of the class? Or is there some other method I can use?

Oliver
  • 11,297
  • 18
  • 71
  • 121

2 Answers2

1

An accepted approach is to use a partial view for the modal div. This does not require an instance of the model.

It is also possible to specify the generic type parameters of the EditorFor method and create a new HtmlHelper. The snippet below was placed in the LogOn.cshtml view that comes with the MVC3 project template.

@(EditorExtensions.EditorFor<MvcApplication9.Models.RegisterModel, string>(new HtmlHelper<MvcApplication9.Models.RegisterModel>(ViewContext, this), m => m.Email))
Community
  • 1
  • 1
barry
  • 835
  • 6
  • 14
0

I decided to create a method to generate the data-val attributes for an input manually. Here it is:

public static MvcHtmlString DataAttributesFor<TMdl>(Expression<Func<TMdl,object>> exp)
{
    string prop;
    if (exp.Body is UnaryExpression)
        prop = ((exp.Body as UnaryExpression).Operand as MemberExpression).Member.Name;
    else if (exp.Body is MemberExpression)
        prop = (exp.Body as MemberExpression).Member.Name;
    else
        throw new ArgumentException("Given selector expression is unsupported.");


    var attrs = typeof(TMdl).GetProperty(prop).GetCustomAttributes();
    var dict = new Dictionary<string,string>();
    foreach (var elm in attrs)
    {
        if (elm.GetType() == typeof(RequiredAttribute))
            dict.Add("data-val-required", ((RequiredAttribute)elm).FormatErrorMessage(null));
        else if (elm.GetType() == typeof(RegularExpressionAttribute))
        {
            var re = (RegularExpressionAttribute)elm;
            dict.Add("data-val-regex", re.FormatErrorMessage(null));
            dict.Add("data-val-regex-pattern", re.Pattern);
        }
        else if (elm.GetType() == typeof(StringLengthAttribute))
        {
            var sl = (StringLengthAttribute)elm;
            dict.Add("data-val-length", sl.MaximumLength.ToString());
            dict.Add("data-val-length-max", sl.FormatErrorMessage(null));
        }
    }

    if (dict.Count == 0)
        return null;

    var sb = new StringBuilder("data-val='true'");
    foreach (var rr in dict)
        sb.Append(" " + rr.Key + "='" + rr.Value + "'");

    return new MvcHtmlString(sb.ToString());            
}

You use it like this:

<input id="record-name" type="text" name="record-name"
 @(HtmlExtensions.DataAttributesFor<PhoneNumberVm>(elm => elm.Name))
/>

The method is not very robust and does not work for all of the data annotation attributes, but it might provide a useful starting point for someone.

Oliver
  • 11,297
  • 18
  • 71
  • 121