问题
I have a partial view that uses a list (CheckBoxListModel) of classes (CheckBoxModel) with a string and bool to create a list of checkboxes. The code works to create the checkboxes and sends the selected ones back to the controller when the page posts. I am trying to find a way to make my partial reusable. As you can see in the code I send the partial the full Model and this works to get the updated checkboxes when the page posts. I tried to send my Model's CheckBoxListModel, but it does not work because when it creates the checkboxes the name is incorrect. I would like to reuse the partial by sending it a CheckBoxListModel so I do not have to create a separate partial every time I need a set of checkboxes.
I tried to change_CheckBoxListPartial.cshtml to
@model MySite.Models.ViewModels.CheckBoxListModel
...
@Html.EditorFor(x => x.CheckBoxes)
...
but without the clmReturnOptions the checkbox names end up as name="CheckBoxes[0].isChecked" instead of name="clmReturnOptions.CheckBoxes[0].isChecked" so they are not updated in the Model when the page posts and gets back to the controller.
I have been looking at: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/ but still can't seem to get the checkboxes to work without sending the entire model to my partial.
CheckBoxListModel.cs
public class CheckBoxListModel: ICheckBoxList
{
public IList<CheckBoxModel> CheckBoxes { get; set; }
public string CheckBoxListTitle { get; set; }
public CheckBoxListModel()
{
}
}
public class CheckBoxModel
{
public string CheckBoxName { get; set; }
public string DisplayName { get; set; }
public bool isChecked { get; set; }
public CheckBoxModel()
{ }
public CheckBoxModel(string checkboxname, string displayname, bool ischecked)
{
CheckBoxName = checkboxname;
DisplayName = displayname;
isChecked = ischecked;
}
}
public interface ICheckBoxList
{
IList<CheckBoxModel> CheckBoxes { get; set; }
string CheckBoxListTitle { get; set; }
}
ReportFilterViewModel.cs
public class ReportFilterViewModel
{
public ReportFilterViewModel()
{
clmReturnOptions = new CheckBoxListModel();
}
public CheckBoxListModel clmReturnOptions { get; set; }
}
filters.cshtml <-- this is where the partial is called
@model MySite.Areas.Reports.Models.ViewModels.ReportFilterViewModel
...
@if (Model.Filters.IsReturnsOptionsAvailable)
{
Html.RenderPartial("_CheckBoxFilterPartial", Model.clmReturnOptions);
}
...
_CheckBoxFilterPartial.cshtml
@model MySite.Areas.Reports.Models.ViewModels.ICheckBoxList
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>Returns Options</title>
</head>
<body>
<div class="col-md-4">
<div class="plm prm ptm pbm configureCellSplitBG configureCellSplitBG-outline mtm">
<div class="row mlm mrm">
<h6>@Model.CheckBoxListTitle</h6>
</div>
@Html.EditorFor(x => x.CheckBoxes)
</div>
</div>
</body>
</html>
CheckBoxModel.cshtml
@model MySite.Areas.Reports.Models.ViewModels.CheckBoxModel
<div class="row mlm mrm">
<div class="form-group">
<label class="checkbox">
@Html.CheckBoxFor(x => x.isChecked, new { @data_toggle = "checkbox" })
@Html.LabelFor(x => x.CheckBoxName, Model.DisplayName)
@Html.HiddenFor(x => x.CheckBoxName)
</label>
</div>
</div>
UPDATE
When I view source I can see that the CheckBox names are still: name="CheckBoxes[0].isChecked"
So when the model gets back to the controller the list is null
1 other change I made was moving the CheckBoxListModel.cs from MySite.Models.ViewModels to MySite.Areas.Reports.Models, since everything else is under the reports.models.
The problem seems to be the partial view. If I put @Html.EditorFor(x => x.clmReturnOptions.CheckBoxes)
in my main page the checkboxes are created with the full name and are updated correctly. As soon as i tried to use the EditorFor in the partial view the checkbox name changes and the link to them back to the Model breaks. I would like to have this in a partial view so I do not have to add all the ui formating everywhere i want a checkbox list.
I have updated the above code
回答1:
You need to pass the prefix to the partial view so the elements are correctly named
@if (Model.Filters.IsReturnsOptionsAvailable)
{
Html.RenderPartial("_CheckBoxFilterPartial", Model.clmReturnOptions, new ViewDataDictionary
{
TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "clmReturnOptions" }
})
}
You can also write a custom html helper to make this a little easier
public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string partialViewName)
{
string name = ExpressionHelper.GetExpressionText(expression);
object model = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).Model;
var viewData = new ViewDataDictionary(helper.ViewData)
{
TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = name }
};
return helper.Partial(partialViewName, model, viewData);
}
and use as
@Html.PartialFor(m => m.clmReturnOptions, "_CheckBoxFilterPartial")
回答2:
Create an interface that provides access to the IList<CheckBoxModel>
public interface ICheckBoxList
{
IList<CheckBoxModel> CheckBoxes { get; set; }
}
Have CheckBoxListModel
implement that interface
public class CheckBoxListModel:ICheckBoxList
{...
Your _CheckBoxListPartial.cshtml partial view will use the new interface as its model
@model MySite.Areas.Reports.Models.ViewModels.ICheckBoxList
and change your EditorFor
to
@Html.EditorFor(x => x.CheckBoxes)
I couldn't find the code in your question that showed how you are including the _CheckBoxListPartial partial view, but you would simply pass the clmReturnOptions
property of the ViewModel (ReportFilterViewModel
or otherwise) instead of the entire model.
And you should be good to go.
来源:https://stackoverflow.com/questions/27967080/reusable-checkbox-partial-view