使用HTML帮助程序时,列表模型绑定索引不正确

Dav*_*ave 3 html c# asp.net-mvc razor

这是一个棘手的解释,所以我会尝试子弹指向.

问题:

  1. View上的用户可用的动态行(集合)(添加/删除)
  2. 用户删除行和保存(POST)
  3. 集合通过非顺序索引传递回控制器
  4. 单步执行代码,一切看起来都很好,收集项目,索引等.
  5. 呈现页面后,项目无法正确显示 - 它们全部为1,因此会复制新0位置的顶部项目.

我发现了什么:

只有在Razor代码中使用HTML Helpers时才会发生这种情况.如果我使用传统<input>元素(不理想),它工作正常.

题:

有没有人遇到过这个问题?或者有人知道为什么会这样,或者我做错了什么?

请查看下面的代码并感谢您查看我的问题!

控制器:

    [HttpGet]
    public ActionResult Index()
    {
        List<Car> cars = new List<Car>
        {
            new Car { ID = 1, Make = "BMW 1", Model = "325" },
            new Car { ID = 2, Make = "Land Rover 2", Model = "Range Rover" },
            new Car { ID = 3, Make = "Audi 3", Model = "A3" },
            new Car { ID = 4, Make = "Honda 4", Model = "Civic" }

        };

        CarModel model = new CarModel();
        model.Cars = cars;

        return View(model);
    }

    [HttpPost]
    public ActionResult Index(CarModel model)
    {
        // This is for debugging purposes only
        List<Car> savedCars = model.Cars;

        return View(model);
    }
Run Code Online (Sandbox Code Playgroud)

Index.cshtml:

如您所见,我有"Make"和"Actual Make"输入.一个是HTML助手,另一个是传统的HTML输入.

    @using (Html.BeginForm())
{
    <div class="col-md-4">

        @for (int i = 0; i < Model.Cars.Count; i++)
        {
            <div id="car-row-@i" class="form-group row">
                <br />
                <hr />

                <label class="control-label">Make (@i)</label>
                @Html.TextBoxFor(m => m.Cars[i].Make, new { @id = "car-make-" + i, @class = "form-control" })

                <label class="control-label">Actual Make</label>
                <input class="form-control" id="car-make-@i" name="Cars[@i].Make" type="text" value="@Model.Cars[i].Make" />

                <div>
                    <input type="hidden" name="Cars.Index" value="@i" />
                </div>

                <br />

                <button id="delete-btn-@i" type="button" class="btn btn-sm btn-danger" onclick="DeleteCarRow(@i)">Delete Entry</button>

            </div>
        }

        <div class="form-group">
            <input type="submit" class="btn btn-sm btn-success" value="Submit" />
        </div>

    </div>
}
Run Code Online (Sandbox Code Playgroud)

Javascript删除功能

function DeleteCarRow(id) {

    $("#car-row-" + id).remove();

}
Run Code Online (Sandbox Code Playgroud)

用户界面中发生了什么:

步骤1 第1步(删除行) 第2步 第2步(提交表格) 第3步 第3步(结果)

小智 5

此行为的原因是HtmlHelper方法使用来自ModelState(如果存在)的值来设置value属性而不是实际的模型值.在TextBoxFor显示初始值的答案中解释了此行为的原因,而不是从代码更新的值.

在您的情况下,提交时,会添加以下值 ModelState

Cars[1].Make: Land Rover 2
Cars[2].Make: Audi 3
Cars[3].Make: Honda 4
Run Code Online (Sandbox Code Playgroud)

请注意,没有值,Cars[0].Make因为您删除了视图中的第一个项目.

当您返回视图时,该集合现在包含

Cars[0].Make: Land Rover 2
Cars[1].Make: Audi 3
Cars[2].Make: Honda 4
Run Code Online (Sandbox Code Playgroud)

因此,在循环的第一次迭代中,TextBoxFor()方法检查ModelState匹配,找不到,并生成value="Land Rover 2"(即模型值),您的手动输入也读取模型值和集value="Land Rover 2"

在第二次迭代中,TextBoxFor()确实找到匹配Cars[1]Make,ModelState因此它设置value="Land Rover 2"和手动输入读取模型值和集合value="Audi 3".

我假设这个问题是只是为了解释行为(在现实中,你会保存数据,然后重定向到GET方法来显示新的列表),但是当你通过调用返回的视图中可以产生正确的输出ModelState.Clear(),其将清除所有ModelState值,以便根据模型值TextBoxFor()生成value属性.

旁注:您的视图包含许多不良操作,包括用行为污染您的标记(使用Unobtrusive JavaScript),创建不表现为标签的标签元素(点击它们不会将焦点设置为关联控件),不必要的使用<br/>元素(使用css为元素设置边距等)和不必要的使用new { @id = "car-make-" + i }.循环中的代码可以是

@for (int i = 0; i < Model.Cars.Count; i++)
{
    <div class="form-group row">
        <hr />
        @Html.LabelFor(m => m.Cars[i].Make, "Make (@i)")
        @Html.TextBoxFor(m => m.Cars[i].Make, new { @class = "form-control" })
        ....
        <input type="hidden" name="Cars.Index" value="@i" />
        <button type="button" class="btn btn-sm btn-danger delete">Delete Entry</button>
    </div>
}

$('.delete').click(function() {
    $(this).closest('.form-group').remove();
}
Run Code Online (Sandbox Code Playgroud)