使用HTML帮助程序

时间:2016-02-24 15:05:42

标签: c# html asp.net-mvc razor

这是一个很难解释的问题,所以我会试着指点。

问题:

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

    只有在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);
        }
    

    Index.cshtml:

    如您所见,我有“制作”和“实际制作”输入。一个是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>
    }
    

    Javascript删除功能

    function DeleteCarRow(id) {
    
        $("#car-row-" + id).remove();
    
    }
    

    用户界面发生了什么:

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

1 个答案:

答案 0 :(得分:5)

此行为的原因是HtmlHelper方法使用ModelState中的值(如果存在)来设置value属性而不是实际模型值。 TextBoxFor displaying initial value, not the value updated from code的答案解释了这种行为的原因。

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

Cars[1].Make: Land Rover 2
Cars[2].Make: Audi 3
Cars[3].Make: Honda 4

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

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

Cars[0].Make: Land Rover 2
Cars[1].Make: Audi 3
Cars[2].Make: Honda 4

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

在第二次迭代中,TextBoxFor()确实在Cars[1]Make中找到ModelState的匹配项,因此设置value="Land Rover 2"并且手动输入读取模型值并设置{{1} }}

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

附注:您的视图包含许多不良做法,包括使用行为污染您的标记(使用Unobtrusive JavaScript),创建不符合标签的标签元素(点击它们不会将焦点设置为关联控制),不必要地使用value元素(使用css来设置带边距的元素等)以及不必要地使用<br/>。循环中的代码可以是

new { @id = "car-make-" + i }