How do you properly bind a Dictionary and it’s values per key to checkboxes?
I can display them in the HTTPGET but binding the selected values again to HTTPPOST doesn’t seem to work.
viewmodel
public class EditViewModel
{
public Foo Foo { get; set; }
public Dictionary<Bar, List<BarVersionEditVM>> Matrix { get; set; }
}
public class BarVersionEditVM
{
public int ID { get; set; }
public string Name { get; set; }
public string Version { get; set; }
public bool IsSupported { get; set; }
}
view:
<form asp-action="Edit">
<div class="row">
@foreach (var kvp in Model.Matrix.OrderByDescending(x => x.Key.Name))
{
<div class="col-md-2 col-lg-2">
<fieldset>
<legend>@kvp.Key.Name</legend>
@foreach (var version in kvp.Value)
{
<div>
<input type="checkbox" id="@version.ID" value="@version.IsSupported" name="@version.Name" @(version.IsSupported ? "checked="checked"" : "") />
<label>@version.Version:</label>
</div>
}
</fieldset>
</div>
}
</div>
<input type="hidden" asp-for="@Model.Foo.ID" />
<input type="submit" value="Save" class="btn btn-default" />
</form>
In the View I tried also to rewrite with foreach and using Html helpers, but without success:
@Html.CheckBoxFor(model => model.Matrix[kvpair.Key][i].IsSupported)
controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(EditViewModel vm) {
// vm is there but Matrix are null.
// and only the ID of Foo property is filled in.
}
any suggestions?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
Unless your Dictionary has simple value types for both the Key and Value (e.g. public Dictionary<string, string>), the DefaultModelBinder requires that the form control name attributes be in the format
<input .... name="Matrix[0].Key" value="..." /> <input .... name="Matrix[0].Value[0].ID" value="..." /> <input .... name="Matrix[0].Value[0].Name" value="..." />
There are no HtmlHelper methods that will generate the correct html to allow binding to your Dictionary.
It is far simpler to create simple view model(s) to with IList<T> properties for the collections. Based on the view you have shown, those models would be
public class EditVM
{
public int FooID { get; set; }
public List<BarVM> Bars { get; set; }
}
public class BarVM
{
public string Name { get; set; }
public List<BarVersionVM> Versions { get; set; }
}
public class BarVersionVM
{
public int ID { get; set; }
public string Name { get; set; } // not clear where you use this property
public string Version { get; set; }
public bool IsSupported { get; set; }
}
and your view would then be
@model EditVM
....
@Html.HiddenFor(m => m.FooID)
@for(int i = 0; i < Model.Bars.Count; i++)
{
<fieldset>
<legend>@Model.Bars[i].Name</legend>
@Html.HiddenFor(m => m.Bars[i].Name) // in case you need to return the view in the POST method
@for(int j = 0; j < Model.Bars[i].Versions.Count; j++)
{
<div>
@Html.HiddenFor(m => m.Bars[i].Versions[j].ID)
@Html.CheckBoxFor(m => m.Bars[i].Versions[j].IsSupported)
@Html.LabelFor((m => m.Bars[i].Versions[j].IsSupported, Model.Bars[i].Versions[j].Version)
</div>
}
</fieldset>
}
<input type="submit" value="Save" />
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0