Question

I am having horrific difficulty in getting unobtrusive validation working on a dropdown list in MVC4.

The main symptom that I am seeing is that the data-val attributes are not rendered on the dropdown element.

The root cause is that the DropDownFor is looking at the editor template's model which does not have any validation attributes on it. The validation is on the parent object property / main model. I am using the dropdown implementation suggested by @DarinDimitrov in this post: https://stackoverflow.com/a/17760548/89092

Does anybody know of a pattern for implementing a dropdown box based on a model value that is a list of key pairs with a selected flag AND having this unobtrusively validated?

I'd love to be able to call Html.GetUnobtrusiveValidationAttributes() in the editor template, append the attributes and keep the code that I have - but for the life of me I can't figure out how to get any data from it - I think it is because the ViewData.ModelMetadata that I have at this point is scoped to the DropDownValues type and not to the main model that has the decorated property test_dd, but if anybody knows what riddle to feed into this little black box then that would be great.

My Model:

...
[Required]
[UIHint("SelectListItemDD")]    
public DropDownValues test_dd { get; set; }
...

This is the DropDownValues view model

public class DropDownValues : IDropDownValues
{
    public string SelectedValue { get; set; }
    public IEnumerable<SelectListItem> Items { get; set; }
}

My Editor Template view model

@model  DropDownValues
@{
    string initial = (ViewData.ModelMetadata).AdditionalValues["InitialValue"] as string;
}
@{if (Model != null && Model.Items != null && Model.Items.Count() > 0)
  {
    @Html.DropDownListFor(x => x.SelectedValue,
    new SelectList(Model.Items, "Value", "Text"), initial)
  }
}
Was it helpful?

Solution

I'd love a real answer to this question - in the meantime, here is my workaround.

Even though I have developed a Locale aware Required Attribute, I had to duplicate the functionality of this in my model (I have a custom resource provider that only delivers Global Resources so this doesn't work with the current MVC localisation scheme for attributes :-((( )

Using the localised message from my model I manually added the data-val attributes :-(((

Here is the editor template:

@model  DropDownValues

@{
    string initial = null;

    if (Model != null && !string.IsNullOrWhiteSpace(Model.InitialValue))
    {
        initial = Model.InitialValue;
    }

    object attrributes;
}

@{if (Model != null && Model.Items != null && Model.Items.Count() > 0)
  {
      if (Model.Required)
      {
          attrributes = new
          {
              data_val_required = Model.RequiredValidationMessage,
              data_val = "true"
          };
      }
      else
      {
          attrributes = null;
      }

    @Html.DropDownListFor(x => x.SelectedValue,
    new SelectList(Model.Items, "Value", "Text"), initial, attrributes)
  }
}

OTHER TIPS

I was having the same problem and found a solution. When I called GetUnobtrusiveValidationAttributes with just the 'name' parameter, it didn't return any attributes. If I added the metadata attribute, it returned the attributes. Here's an example call:

public static MvcHtmlString DropDownListCustom<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IEnumerable<SelectListItem> selectList, IDictionary<string, object> htmlAttributes)
{
    var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
    var atts = html.GetUnobtrusiveValidationAttributes(metadata.PropertyName, metadata);
}

In my case, the 'atts' dictionary now had 2 values for a required field: data-val and data-val-required. I had 2 required fields but only one was working normally. This is the code I added after the GetUnobtrusiveValidationAttributes call:

    if (atts != null && atts.Count > 0)
    {
        foreach (var attr in atts)
        {
            htmlAttributes.Add(attr.Key, attr.Value);
        }
    }

...and then later down this call:

    builder.AppendLine(html.DropDownList(metadata.PropertyName, selectList, htmlAttributes).ToString());

Note that once you call GetUnobtrusiveValidationAttributes, if you try calling it again, you get nothing. That's why I had to add the attributes myself in all cases.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top