首页 > 解决方案 > Razor 代码在遍历复选框列表时产生意外结果

问题描述

这个剃刀代码可能有什么原因:

// 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,whenModel.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 的值,而 6653 里面的值<span>呢?

这是视图的控制器方法:

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
}

更新

澄清此操作的流程:

  1. 索引视图包含一个带有Selected列表中每个成员的复选框的表单。

  2. 此表单将整个成员列表传递给控制器​​方法ShowSelectedMembers

  3. 控制器方法过滤出Selected设置为的成员false,并将选定的成员传递给视图ShowSelectedMembers

  4. 该视图显示过滤后的列表,该列表现在已损坏数据。

更新 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.

标签: c#razorasp.net-core-mvcasp.net-core-2.2

解决方案


在您的第一个示例中,您可以添加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);
}

但是,我会按照这里的建议https://stackoverflow.com/a/1775185/2030565并采用 PRG 模式——发布表单然后重定向到 GET 操作。如果无法从数据库中重新水化模型,则需要将模型存储在 TempData 存储中。


推荐阅读