当捕获输入记录时,我如何正确地使用ViewModel与视图建立双向绑定?
本文关键字:视图 ViewModel 建立 绑定 正确地 记录 输入 | 更新日期: 2023-09-27 18:08:42
我有一个表单,需要从表单中的复选框中捕获值。每个复选框应该有一个整数值,当提交表单时,视图模型应该验证这些值,并且至少应该选择一个。
我还需要构建一个双向绑定,以便框架将自动选择在页面加载时所选择的选项。
这是我的模型的样子
public class SomeViewModel
{
[Required(ErrorMessage = "You must select at least one site.")]
[Display(Name = "All site to have access too")]
public int[] Sites { get; set; }
}
我将ViewModel封装在一个名为Presenter
的Presentation类中,如下所示
public class Presenter
{
public SomeViewModel Access { get; set; }
public IEnumerable<Site> AvailableSites { get; set; }
}
现在,我将Presenter
传递给我的视图,并希望渲染
我的视图是这样的
<div class="form-group">
@Html.LabelFor(m => m.Access.Sites, new { @class = "col-sm-2 control-label" })
<div class="col-sm-10">
@for (int i = 0; i < Model.AvailableSites.Count(); i++ )
{
<label class="radio-inline">
@Html.CheckBoxFor(m => m.Access.Sites[i])
</label>
}
</div>
@Html.ValidationMessageFor(m => m.Access.Sites)
</div>
由于@Html.CheckBoxFor
接受bool值,并且我正在传递一个整数,我在视图内的@Html.CheckBoxFor(m => m.Access.Sites[i])
行上得到错误。
如何纠正这个问题?以及如何在此视图中正确呈现复选框?
正如您所发现的,您只能使用CheckBoxFor()
绑定到bool
属性。它有点不清楚为什么你有两个视图模型当你可以通过使用
public class Presenter
{
[Required(ErrorMessage = "You must select at least one site.")]
[Display(Name = "All site to have access too")]
public int[] Sites { get; set;
public IEnumerable<Site> AvailableSites { get; set; }
}
您可以考虑的一个选项(基于上述模型并假设Site
类型包含属性int ID
和string Name
)
将视图更改为手动生成<input type="checkbox" />
元素
@foreach(var site in Model.AvailableSites)
{
// assumes your using Razor v2 or higher
bool isSelected = Model.Sites.Contains(s.ID);
<label>
<input type="checkbox" name="Sites" value="@site.ID" checked = @isSelected />
<span>@s.Name</span>
</label>
}
@Html.ValidationMessageFor(m => m.Sites)
注意@Html.LabelFor(m => m.Sites)
是不合适的。<label>
是一个可访问性元素,用于将焦点设置为与其关联的表单控件,而Sites
没有窗体控件。你可以使用<div>@Html.DisplayNameFor(m => m.Sites)</div>
一个更好的选择(和MVC方式)是创建一个视图模型来表示你想要在视图
public class SiteVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
和在GET方法中,基于所有可用站点(例如
)返回List<SiteVM>
List<SiteVM> model = db.Sites.Select(x => new SiteVM() { ID = x.ID, Name = x.Name };
return View(model);
并在视图中使用EditorTemplate
的for
循环(详细信息请参考此答案)来生成视图
@model List<SiteVM>
....
@using (Html.BeginForm())
{
for(int i = 0; i < Model.Count; i++)
{
@Html.HiddenFor(m => m[i].ID)
@Html.HiddenFor(m => m[i].Name)
<label>
@Html.CheckBoxFor(m => m[i].IsSelected)
<span>@Model[i].Name</span>
</label>
@Html.ValidationMessage("Sites")
}
....
}
,然后在POST方法
[HttpPost]
public ActionResult Edit(List<SiteVM> model)
{
if (!model.Any(x => x.IsSelected))
{
ModelState.AddModelError("Sites", "Please select at least one site");
}
if (!ModelState.IsValid)
{
return View(model);
}
...
}
在任何一种情况下,您都不能使用jquery.validate.unobtrusive
获得客户端验证,因为您不能(也不能)为集合属性创建表单控件。但是,您可以编写自己的脚本来显示验证消息并取消默认提交,例如(假设您使用@Html.ValidationMessageFor(m => m.Sites, "", new { id = "sitevalidation" })
)
var sitevalidation = $('#sitevalidation');
$('form').submit(function() {
var isValid = $('input[type="checkbox"]:checked').length > 0;
if (!isValid) {
sitevalidation.text('Please select at least one site');
return false; // cancel the submit
}
}
$('input[type="checkbox"]').click(function() {
var isValid = $('input[type="checkbox"]:checked').length > 0;
if (isValid) {
sitevalidation.text(''); // remove validation message
}
}