EditorFor() and additionalViewData: how to add data in helper class?

Cymen picture Cymen · Jun 9, 2011 · Viewed 27k times · Source

EditorFor() can take an object additionalViewData parameter which the typical method to populate is something like:

EditorFor(model => model.PropertyName, new { myKey = "myValue" })

How can I inspect the contents of additionalViewData, add or append to existing value for a key, etc in a custom HTML Helper?

I've tried these approaches:

  • convert to Dictionary<string, object>() and add/append values: doesn't work as it looks like the implementation of EditorFor in MVC uses new RouteValueDictionary(additionalViewData) which embeds the dictionary within a dictionary
  • convert to RouteValueDictionary using new RouteValueDictionary(additionalViewData) but that has same (or very similar) issue as above

I'm also open to "you're doing it wrong" -- maybe I'm missing a simpler approach. Keep in mind what I'm trying to do is write an HTML helper that is reusable and adds some values to the additionalViewData to be used by custom views. Some of the values depend on metadata from the property so it is not quite so easy as just use a bunch of different templates.

Update with example of what I'm doing:

    public static MvcHtmlString myNullableBooleanFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> choice, string templateName, object additionalViewData)
    {            
        ModelMetadata metadata = ModelMetadata.FromLambdaExpression(choice, htmlHelper.ViewData);

        /*
    here need to add to additionalViewData some key values among them:
    { propertyName, metadata.PropertyName }

     */

        StringBuilder sb = new StringBuilder();
        sb.AppendLine(htmlHelper.EditorFor(choice, templateName, additionalViewData).ToString());
        MvcHtmlString validation = htmlHelper.ValidationMessageFor(choice);
        if (validation != null)
            sb.AppendLine(validation.ToString());
        return new MvcHtmlString(sb.ToString());
    }

Update with what happens when I convert the anonymous object to a Dictionary<string, object>() and pass that dictionary to EditorFor():

I put a break point in the Razor view and examined ViewData. It appears that the dictionary passed into EditorFor() is put inside another Dictionary<string, object>(). In the "Immediate Window", ViewData looks like this:

ViewData.Keys
Count = 4
    [0]: "Comparer"
    [1]: "Count"
    [2]: "Keys"
    [3]: "Values"

See how the dictionary has the contents of a dictionary within it? Yes, the actual data is in that inner dictionary however unsurprisingly, this doesn't work.

Added bounty.

Answer

jhilden picture jhilden · Feb 22, 2012

in my case I have an editor for a boolean field that I want to be a yes/no radio. I use the additionalViewData property to set the text of yes/no to be localized. Seems to work great for me!

Here is the code for my custom editorFor:

@model bool?       
@{
    var yes = ViewBag.TrueText ?? @Resources.WebSiteLocalizations.Yes;
    var no = ViewBag.FalseText ?? @Resources.WebSiteLocalizations.No;
}
<div class="title">
    @Html.LabelFor(model => model)
    @Html.RequiredFor(model => model)
</div>
<div class="editor-field">
    @Html.RadioButton("", "True", (Model.HasValue && Model.Value))@yes
    <br />
    @Html.RadioButton("", "False", (Model.HasValue && Model.Value == false))@no
    <br />
    @Html.ValidationMessageFor(model => model)
</div>
@Html.DescriptionFor(model => model)   

Here is how you call this custom EditorFor:

@Html.EditorFor(model => model.IsActive, new {TrueText = @Resources.WebSiteLocalizations.Active, FalseText = @Resources.WebSiteLocalizations.InActive})