Razor 代码在遍历复选框列表时产生意外结果
Razor code producing unexpected result when looping through a list of checkboxes
这个剃须刀代码可能有什么原因:
// ShowSelectedMembers.cshtml
@model MyApp.Models.MembersViewModel
@for (int m = 0; m < Model.Members.Count; m++)
{
<input type="hidden" asp-for="@Model.Members[m].Id" />
<input type="hidden" asp-for="@Model.Members[m].FirstName" />
<input type="hidden" asp-for="@Model.Members[m].LastName" />
<span>[debug-info: m = @m, id = @Model.Members[m].Id]</span>
<span>@Model.Members[m].LastName</span>,
<span>@Model.Members[m].FirstName</span>
}
生成此 HTML,当 Model.Members.Count = 1
时,仅包含 ID 为 6653 的成员:
<input type="hidden" data-val="true" data-val-required="The Id field is required." id="Members_0__Id" name="Members[0].Id" value="6652" />
<input type="hidden" id="Members_0__FirstName" name="Members[0].FirstName" value="Peter" />
<input type="hidden" id="Members_0__LastName" name="Members[0].LastName" value="Hanson" />
<span>[debug-info: m = 0, id = 6653]</span>
<span>Swanson</span>,
<span>Lisa</span>
Members[0].Id
怎么可能在隐藏字段中的值为6652,在<span>
中的值为6653?
这是视图的控制器方法:
public IActionResult ShowSelectedMembers(MembersViewModel vm)
{
vm.Members = vm.Members.Where(s => s.Selected).OrderBy(o => o.LastName).ThenBy(o => o.FirstName).ToList();
return View(vm);
}
这是将整个成员列表发送到控制器方法的表单:
// Index.cshtml
@model MyApp.Models.MembersViewModel
<form asp-action="ShowSelectedMembers">
<button type="submit">View selection</button>
@if (Model.Members.Any())
{
for (int i = 0; i < Model.Members.Count; i++)
{
Model.Members[i].Id
Model.Members[i].FirstName
Model.Members[i].LastName
<input type="checkbox" asp-for="@Model.Members[i].Selected" />
<input type="hidden" asp-for="@Model.Members[i].Id" />
<input type="hidden" asp-for="@Model.Members[i].FirstName" />
<input type="hidden" asp-for="@Model.Members[i].LastName" />
}
}
</form>
当表单数据发送到控制器方法时,它包含该视图中的所有成员。然后该方法过滤掉所有 Selected
设置为 false
.
的成员
这些是 ViewModel:
public class MembersViewModel
{
// ... some more properties
public List<MemberViewModel> Members { get; set; }
}
public class MemberViewModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Selected { get; set; }
// ... some more properties
}
更新
阐明此操作的流程:
索引视图包含一个表单,列表中的每个成员都有 Selected
个复选框。
此表单将整个成员列表传递给控制器方法 ShowSelectedMembers
。
控制器方法过滤掉 Selected
设置为 false
的成员,并将选择的成员传递给视图 ShowSelectedMembers
.
视图显示过滤后的列表,现在数据已损坏。
更新 2
我将控制器方法更改为此,创建一个新实例而不是重新使用视图模型:
public async Task<IActionResult> ShowSelectedMembers(MembersViewModel vmIn)
{
List<int> memberIds = new List<int>();
foreach (MemberViewModel member in vmIn.Members.Where(s => s.Selected).ToList())
{
memberIds.Add(member.Id);
}
List<Member> selectedMembers = await db.Members
.Where(s => memberIds.Contains(s.Id))
.ToListAsync();
MembersViewModel vmOut = new MembersViewModel {
Members = auto.Map<List<MemberViewModel>>(selectedMembers)
};
return View(vmOut);
}
...但是结果完全一样
更新 3
这太奇怪了!
当我在 ShowSelectedMembers.cshtml
中将剃须刀代码更改为此时,它似乎工作正常:
@*<input type="text" asp-for="@Model.Members[i].Id" />*@
@{
string _id = $"Members_{i}__Id";
string _name = $"Members[{i}].Id";
string _value = Model.Members[i].Id.ToString();
}
<input type="hidden"
data-val="true"
data-val-required="The Id field is required."
id=@_id
name=@_name
value=@_value />
即手动生成输入字段而不是使用 asp-for
.
在您的第一个示例中,您可以添加 ModelState.Clear()
,它应该会为您提供所需的结果。
public IActionResult ShowSelectedMembers(MembersViewModel vm)
{
ModelState.Clear();
vm.Members = vm.Members.Where(s => s.Selected).OrderBy(o => o.LastName).ThenBy(o => o.FirstName).ToList();
return View(vm);
}
但是,我会遵循此处的建议 并采用 PRG 模式 -- POST 表单然后重定向到 GET 操作。如果您无法从数据库中重新水化模型,则需要将模型存储在 TempData 存储中。
这个剃须刀代码可能有什么原因:
// ShowSelectedMembers.cshtml
@model MyApp.Models.MembersViewModel
@for (int m = 0; m < Model.Members.Count; m++)
{
<input type="hidden" asp-for="@Model.Members[m].Id" />
<input type="hidden" asp-for="@Model.Members[m].FirstName" />
<input type="hidden" asp-for="@Model.Members[m].LastName" />
<span>[debug-info: m = @m, id = @Model.Members[m].Id]</span>
<span>@Model.Members[m].LastName</span>,
<span>@Model.Members[m].FirstName</span>
}
生成此 HTML,当 Model.Members.Count = 1
时,仅包含 ID 为 6653 的成员:
<input type="hidden" data-val="true" data-val-required="The Id field is required." id="Members_0__Id" name="Members[0].Id" value="6652" />
<input type="hidden" id="Members_0__FirstName" name="Members[0].FirstName" value="Peter" />
<input type="hidden" id="Members_0__LastName" name="Members[0].LastName" value="Hanson" />
<span>[debug-info: m = 0, id = 6653]</span>
<span>Swanson</span>,
<span>Lisa</span>
Members[0].Id
怎么可能在隐藏字段中的值为6652,在<span>
中的值为6653?
这是视图的控制器方法:
public IActionResult ShowSelectedMembers(MembersViewModel vm)
{
vm.Members = vm.Members.Where(s => s.Selected).OrderBy(o => o.LastName).ThenBy(o => o.FirstName).ToList();
return View(vm);
}
这是将整个成员列表发送到控制器方法的表单:
// Index.cshtml
@model MyApp.Models.MembersViewModel
<form asp-action="ShowSelectedMembers">
<button type="submit">View selection</button>
@if (Model.Members.Any())
{
for (int i = 0; i < Model.Members.Count; i++)
{
Model.Members[i].Id
Model.Members[i].FirstName
Model.Members[i].LastName
<input type="checkbox" asp-for="@Model.Members[i].Selected" />
<input type="hidden" asp-for="@Model.Members[i].Id" />
<input type="hidden" asp-for="@Model.Members[i].FirstName" />
<input type="hidden" asp-for="@Model.Members[i].LastName" />
}
}
</form>
当表单数据发送到控制器方法时,它包含该视图中的所有成员。然后该方法过滤掉所有 Selected
设置为 false
.
这些是 ViewModel:
public class MembersViewModel
{
// ... some more properties
public List<MemberViewModel> Members { get; set; }
}
public class MemberViewModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Selected { get; set; }
// ... some more properties
}
更新
阐明此操作的流程:
索引视图包含一个表单,列表中的每个成员都有
Selected
个复选框。此表单将整个成员列表传递给控制器方法
ShowSelectedMembers
。控制器方法过滤掉
Selected
设置为false
的成员,并将选择的成员传递给视图ShowSelectedMembers
.视图显示过滤后的列表,现在数据已损坏。
更新 2
我将控制器方法更改为此,创建一个新实例而不是重新使用视图模型:
public async Task<IActionResult> ShowSelectedMembers(MembersViewModel vmIn)
{
List<int> memberIds = new List<int>();
foreach (MemberViewModel member in vmIn.Members.Where(s => s.Selected).ToList())
{
memberIds.Add(member.Id);
}
List<Member> selectedMembers = await db.Members
.Where(s => memberIds.Contains(s.Id))
.ToListAsync();
MembersViewModel vmOut = new MembersViewModel {
Members = auto.Map<List<MemberViewModel>>(selectedMembers)
};
return View(vmOut);
}
...但是结果完全一样
更新 3
这太奇怪了!
当我在 ShowSelectedMembers.cshtml
中将剃须刀代码更改为此时,它似乎工作正常:
@*<input type="text" asp-for="@Model.Members[i].Id" />*@
@{
string _id = $"Members_{i}__Id";
string _name = $"Members[{i}].Id";
string _value = Model.Members[i].Id.ToString();
}
<input type="hidden"
data-val="true"
data-val-required="The Id field is required."
id=@_id
name=@_name
value=@_value />
即手动生成输入字段而不是使用 asp-for
.
在您的第一个示例中,您可以添加 ModelState.Clear()
,它应该会为您提供所需的结果。
public IActionResult ShowSelectedMembers(MembersViewModel vm)
{
ModelState.Clear();
vm.Members = vm.Members.Where(s => s.Selected).OrderBy(o => o.LastName).ThenBy(o => o.FirstName).ToList();
return View(vm);
}
但是,我会遵循此处的建议 并采用 PRG 模式 -- POST 表单然后重定向到 GET 操作。如果您无法从数据库中重新水化模型,则需要将模型存储在 TempData 存储中。