Not able to bind html.checkbox on form post

2019-08-06 14:48发布

So I have a view model call ProductViewModel which has a list of sites where a product can be produced. I am trying to figure out how to show this on a form using a checkbox for the user to select the sites where the product can be produced. Seems straight forward, right? Well it doesn't work like I want/need. Hoping someone can help guide me to the correct way to do this.

My classes:

 public class ProductViewModel
 {
    public List<Sites> SiteList {get; set;}   
    public string ProductName {get; set;}
    public int ProductId {get; set;}
    public User ProductOwner{get; set;}
 }

 public class Sites
 { 
    public int SiteId {get; set;} 
    public string SiteName {get; set;}
    public bool IsSelected {get; set;}    
 }

Part of my view:

@Html.LabelFor(m=>m.Sites):
@foreach (var site in Model.Sites)
{
    @Html.CheckBox("Sites", site.IsSelected, new { value = site.SiteName })
    @Html.Label(site.SiteName)
} 

When using @Html.Checkbox() I see the following output in the html from the browser:

<input checked="checked" id="Sites" name="Sites" type="checkbox" value="Miami" />
<input name="Sites" type="hidden" value="false" />

I understand the hidden field but what I really need is to get the value for the selected item. So I need to get back the list with Miami in it. I don't need the false/true thing that the html helper seem to want to send (i.e. Miami=true)

So instead I tried this.

@for(int id=0; id < Model.Sites.Count(); id++)
{
    <input type="checkbox" id="@Model.Sites[id].SiteName" name="Sites[@id]" value="@Model.BoxingSites[id].SiteName" @(Model.Sites[id].IsSelected  ? @"checked=""checked""": "") />
    @Html.Label(Model.Sites[id].SiteName)                   
}  

And the output is:

<input type="checkbox" id="Miami" name="Sites[0]" value="Miami" checked=&quot;checked&quot; />
<label for="Miami">Miami</label>

In both of these cases I am not able to get the binder to map the form values to the Product.Sites list when posting to the action.

The action is like this:

[HttpPost]
public ActionResult Edit(ProductViewModel Product)
{
     //Does something with the form data.
}

The other values (ProductName etc...) map fine.

What am I doing wrong? I feel I am missing something as this should be easier due to how MVC simplifies so many other form handling situations.

Thanks in advance...

2条回答
对你真心纯属浪费
2楼-- · 2019-08-06 15:26

Here is what is working for me.

// View Model
[Display(Name="Boolean Property")]
[UIHint("booleancheckbox"]
public bool? booleanProperty;

View

// View
@Html.EditorFor(m => m.booleanProperty, new { @onclick = "Toggle(this);" })

Editor Template - add some more code to handle null values

// Editor Template booleancheckbox.cshtml
@model bool?

@{
    labelText = ViewData.ModelMetadata.DisplayName != null ?
                ViewData.ModelMetadata.DisplayName : 
                ViewData.ModelMetadata.PropertyName;
}

<label for="@ViewData.ModelMetadata.PropertyName">@labelText

    @Html.CheckBox(string.Empty, Model.Value, ViewContext.ViewData)

</label>
查看更多
时光不老,我们不散
3楼-- · 2019-08-06 15:39

How about using an editor template instead of struggling with loops:

@model ProductViewModel
@using (Html.BeginForm())
{
    ... some other form fields

    @Html.LabelFor(x => x.SiteList)
    @Html.EditorFor(x => x.SiteList)

    <input type="submit" value="Create" />
}

and inside the corresponding editor template ~/Views/Shared/EditorTemplates/Sites.cshtml:

@model Sites
<div>
    @Html.HiddenFor(x => x.SiteId)
    @Html.CheckBoxFor(x => x.IsSelected)
    @Html.LabelFor(x => x.SiteName)
</div>

Now not only that your view code is much more clean but proper names will be generated for the input fields so that the model binder will be able to bind the selected values back in the POST action.

[HttpPost]
public ActionResult Create(ProductViewModel model)
{
    ...
}
查看更多
登录 后发表回答