Serexx
Serexx

Reputation: 1250

MVC Model Binding to Dictionary by Key with HTML Helper

When filtering a data list in MVC(4) I am binding several filter inputs to a dictionary that lives in the ViewModel model.

I've been able to do that with a literal declaration of the input like this (.aspx engine):

 <input type="text" name='Model.Filters[matchId].Filter' 
                    value='<%: Model.Filters["matchId"].Filter %>' />

Where "matchId" is the name of the column being filtered. The Model.Filter dictionary is defined as

IDictionary<string,FilterObject>

with FilterObject for the moment being simply:

[Serializable]
public class FilterObject
{
    public FilterObject()
    {
        Filter = "";
    }
    public FilterObject(String value)
    {
        Filter = value;
    }
    //[DefaultValue("")]
    public String Filter { get; set; }
}

So - that works as long as the Model.Filters Dictionary is initialized with all of the keys for which there Filter inputs, otherwise we naturally get a "key not found" runtime exception.

Hard-coding column-name strings into the controller is ugly and it feels like it should be possible to avoid the exception using an @Html.TextBoxFor<>(....) but I cannot find a syntax that works (or doesn't just break on invalid index object type etc.) for example:

Html.TextBoxFor(m=>m.Filters.FirstOrDefault(k=>k.Key=="matchId").Value.Filter)

Produces HTML of

  <input id="Value_Filter" name="Value.Filter" type="text" value="" />

which clearly does nothing useful

I'm pretty sure I'm just missing something but all the Dictionary binding examples I have found depend on Loops and binding to an int index, and I am at this point stumped, short of writing a custom Helper which seems overkill.

Any help greatly appreciated :-)

Upvotes: 0

Views: 1236

Answers (1)

Serexx
Serexx

Reputation: 1250

Ha! I stopped fighting the tide and wrote a Helper for this, easier than I thought:

public static MvcHtmlString TextBoxForFilterDictionary(this HtmlHelper helper, IDictionary<string, FilterObject> filters, string fieldName, object htmlAttributes = null)
    {
        FilterObject filter;
        if (!filters.TryGetValue(fieldName, out filter))
        {
            filter = new FilterObject();
        }
        string nameAttribute = String.Format("Model.Filters[{0}].Filter", fieldName);
        MvcHtmlString html = helper.TextBox(nameAttribute, filter.Filter, htmlAttributes);
        return html;
    }

And in the HTML:

  <%: Html.TextBoxForFilterDictionary(Model.Filters, "matchId")%>
  <!-- a bunch of other formatting HTML -->
  <%: Html.TextBoxForFilterDictionary(Model.Filters, "matchName")%>
  <!-- and so on -->

My generic ViewModel now carries everything the list needs for sorting, paging, and filtering, no filter Dictionary initialization required and nary a weakly typed object in sight.

Worth the little extra effort!

Upvotes: 1

Related Questions