为什么当我提交表单时,控制器不会从Model.Items接收任何数据? 我使用的是asp.net core mvc 2.0
这是我的View-Model
public class SectionVM
{
public long Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
public ICollection<SectionItemCollector> Items { get; set; }
}
public class SectionItemCollector
{
public bool IsSelected { get; set; }
public long ItemId { get; set; }
public string ItemName { get; set; }
}
在这里,我创建了一个新的视图模型,用数据填充它并将其发送到视图。视图接收视图模型,我目前能够毫无问题地访问视图模型中的所有数据。
// Controller Actions
// GET: MenuSections/Create
public async Task<IActionResult> Create()
{
var viewModel = new SectionVM();
var allItems = await _context.MenuItem.ToListAsync();
var vmItems = new List<SectionItemCollector>();
foreach (var i in allItems)
{
vmItems.Add(new SectionItemCollector() { IsSelected = false, ItemId = i.Id, ItemName = i.Name });
}
viewModel.Items = vmItems;
return View(viewModel);
}
我的视图显示了视图模型中包含的数据,用户可以选择选择一些项目
// View
@model ROO_App.Models.SectionVM
...
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group">
@{
foreach (var item in Model.Items)
{
<div class="row">
<div class="col-sm-1">
<input asp-for="@item.IsSelected" type="checkbox" class="form-check-input" id="@item.ItemId"/>
</div>
<div class="col-sm-4">@item.ItemName</div>
</div>
}
}
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
提交表单时Items = null
// POST: MenuSections/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,Description, Items")] SectionVM menuSection)
{
...
}
答案 0 :(得分:2)
<强>原因:强>
没有名为“Items”的变量被发送到服务器,因为表单不包含名为“Items”的输入。而是通过复选框输入发送名为“item.IsSelected”的数组。
在您的视图中,以下行:(显示重要部分)
<input asp-for="@item.IsSelected"
是一个HTML输入字段的脚手架,如下所示:(显示重要部分)
<input type="checkbox" name="item.IsSelected" value="true">
使用Google Chrome,在提交表单之前按F12。查看“网络”标签&gt;标题&gt;表单数据,您可以确认它为每个选中的项目提交了以下字段。
item.IsSelected:true
因此控制器没有收到名为“Items”的输入。它正在接收名为“item.IsSelected”的字符串数组,其默认值为“true”。
努力寻求解决方案:
您可以通过为输入标记提供可选的“name”参数来覆盖默认名称。
<input asp-for="@item.IsSelected" name="Items"
您还要设置Id属性。在此上下文中,Id用于HTML,不随表单一起提交。仅提交复选框的值。
id="@item.ItemId" />
如果要将已检查的ID作为数组提交,请将其设置为值,而不是id。
value="@item.ItemId" />
如果您更喜欢使用ItemName作为值:
value="@item.ItemName" />
然而,HTML复选框输入将不允许您传递ItemName和Id vales,因为它只传递所选项目的单个值。
由于类型不匹配,MVC控制器将无法将名为“Items”的字符串[]映射到SectionItemCollector类。它可能会检测数组中Items的计数,但值仍为null。
有了这个限制,最好将表单数据序列化为JSON对象并通过AJAX发布。
使用表单帖子解决
要使用表单帖子解决,您需要进行一些更改以绑定和处理表单数据:
将以下属性添加到您的类SectionVM:
public string[] CheckedItems { get; set; }
public string[] CheckedValues { get; set; }
更新Create.cshtml以将isChecked和ID作为复选框值的一部分传递。添加隐藏输入以传递ItemName。
<div class="form-group">
@{
foreach (var item in Model.Items)
{
<div class="row">
<div class="col-sm-1">
@* If checked, the value will be posted as the CheckedItems string[] *@
<input asp-for="@item.IsSelected" name="CheckedItems" type="checkbox" class="form-check-input" value="@item.ItemId" />
@* posting a hidden value as the CheckedValues string[] *@
<input name="CheckedValues" type="hidden" value="@item.ItemName" />
</div>
<div class="col-sm-4">@item.ItemName</div>
</div>
}
}
</div>
最后,更新控制器操作以收集复选框和隐藏值,并将它们重新组合到预期对象中:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,Description, Items, CheckedItems, CheckedValues")] SectionVM menuSection)
{
// convert CheckedItems and CheckedValues into List<SectionItemCollector>
var selectedItems = new List<SectionItemCollector>();
for (int i = 0; i < menuSection.CheckedItems.Length; i++)
{
// if the checked ID is number, find the related ItemName.
long id;
if (long.TryParse(menuSection.CheckedItems[i], out id))
{
var item = new SectionItemCollector
{
ItemId = id,
// inferred as HTML only posts checkboxes that are selected.
IsSelected = true,
// get the ItemName with the same index.
ItemName = menuSection.CheckedValues[i]
};
selectedItems.Add(item);
}
}
var viewmodel = new SectionVM()
{
Items = selectedItems
};
// TODO: Set the breakpoint to the next line and inspect viewmodel to see your result.
return View(viewmodel);
}
也许Asp.Net有另一种方法来抽象提交具有多个属性的复选框的复杂性。如果是这样,我希望有人也提出这个答案。
答案 1 :(得分:1)
这是我的第一篇文章,所以对不起(特别是对于我的英语)
您可以使用标签帮助程序进行收集,并在发布表单时直接获取模型。
这是一个小例子,但我认为这是使视图和控制器代码干净且标准的最佳解决方案:
<div class="row">
@{
foreach (var element in Model.Elements)
{
int index = Model.Elements.IndexOf(element);
<input type="text" asp-for="Elements[index].Text" />
<input type="checkbox" asp-for="Elements[index].IsSelected" />
}
}
</div>
所以就像其他属性一样使用asp-for 并且在控制器中,您的集合在发布后具有直接绑定的元素