MVC3 Razor“For”模型 - 内容重复

时间:2011-12-14 15:45:04

标签: asp.net-mvc-3 razor

令人感兴趣的是,我的MVC3剃刀格式在foreach代码块内呈现重复值,尽管从服务器正确接收数据。这是我在MVC3 Razor中的简单形式......

- 我的.cshtml网页示例

@model List<Category>
@using (@Html.BeginForm("Save", "Categories", FormMethod.Post))
{
foreach (Category cat in Model)
 {
        <span>Test: @cat.CategoryName</span>

        <span>Actual: @Html.TextBoxFor(model => cat.CategoryName)</span>
        @Html.HiddenFor(model => cat.ID)
        <p>---</p>            
 }

  <input type="submit" value="Save" name="btnSaveCategory" id="btnSaveCategory" />
}

我的控制器操作看起来像这样 -

 [HttpPost]
    public ActionResult Save(ViewModel.CategoryForm cat)
    {
       ... save the data based on posted "cat" values (I correctly receive them here)

       List<Category> cL = ... populate category list here
       return View(cL);
    }

上面的保存操作会返回具有正确数据的模型。

在提交上述表格后,我希望在完成行动后能看到类似于以下类别的价值......

Test: Category1, Actual:Category1
Test: Category2, Actual:Category2
Test: Category3, Actual:Category3
Test: Category4, Actual:Category4

@Html.TextBoxFor重复列表中的第一个值。发布表单后,我看到响应如下所示。即使我从服务器获得正确的数据,也会重复“实际”值。

Test: Category1, Actual:Category1
Test: Category2, Actual:Category1
Test: Category3, Actual:Category1
Test: Category4, Actual:Category1

我做错了什么?任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:3)

TextBoxFor这样的辅助方法旨在与表示单个对象的ViewModel一起使用,而不是对象的集合。

正常使用将是:

@Html.TextBoxFor(c => c.Name)

c在方法内部映射到ViewData.Model

你正在做一些不同的事情:

@Html.TextBoxFor(c => iterationItem.Name)

internall方法仍将尝试使用ViewData.Model作为渲染的基础对象,但您打算在迭代项上使用它。该语法虽然对编译器有效,但可以解决这个问题。

解决方法是创建一个对单个项目进行操作的局部视图:在该视图中,您可以使用具有正确语法的html帮助程序(第一个示例),然后在foreach中调用它,将迭代项目作为参数传递。这应该可以正常工作。

答案 1 :(得分:0)

更好的方法是使用EditorTemplates。

在您的表单中,您可以这样做:

@model List<Category>
@using (@Html.BeginForm("Save", "Categories", FormMethod.Post))
{
    @Html.EditorForModel()
    <input type="submit" value="Save" name="btnSaveCategory" id="btnSaveCategory" />
}

然后,您将在〜/ Views / Shared文件夹或Controllers View文件夹中创建名为EditorTemplates的文件夹(取决于您是要与整个应用程序共享模板还是仅与此控制器共享),以及在EditorTemplates文件夹中,创建一个如下所示的Category.cshtml文件:

@model Category
<span>Test: @Model.CategoryName</span>

<span>Actual: @Html.TextBoxFor(model => model.CategoryName)</span>
@Html.HiddenFor(model => model.ID)
<p>---</p>            

MVC将自动遍历集合并为其中的每个项目调用模板。

答案 2 :(得分:0)

我注意到在Views中使用foreach循环会导致文本框的名称属性对于集合中的每个项目呈现相同。对于您的示例,将使用以下ID和名称属性呈现每个文本框:

<input id="cat_CategoryName" name="cat.CategoryName" value="Category1" type="text">

当您的控制器收到表单数据集合时,它将无法将集合重建为不同的值。

解决方案

  1. 我采用的一个好模式是将您的View绑定到要发回的同一个类。在示例中,模型绑定到List<Category>,但控制器Save方法接收模型ViewModel.CategoryForm。我会让它们都一样。

  2. 使用for循环代替foreach。 name / id属性将是唯一的,模型绑定器将能够区分这些值。

  3. 我的最终代码:

    查看

    @model CategoryForm
    @using TestMvc3.Models
    
    @using (@Html.BeginForm("Save", "Categories", FormMethod.Post))
    {
        for (int i = 0; i < Model.Categories.Count; i++)
        {
            <span>Test: @Model.Categories[i].CategoryName</span>
    
            <span>Actual: @Html.TextBoxFor(model => Model.Categories[i].CategoryName)</span>
            @Html.HiddenFor(model => Model.Categories[i].ID)
            <p>---</p>            
        }
    
        <input type="submit" value="Save" name="btnSaveCategory" id="btnSaveCategory" />
    }
    

    <强>控制器

    public ActionResult Index()
    {
        // create the view model with some test data
        CategoryForm form = new CategoryForm()
        {
            Categories = new List<Category>()
        };
    
        form.Categories.Add(new Category() { ID = 1, CategoryName = "Category1" });
        form.Categories.Add(new Category() { ID = 2, CategoryName = "Category2" });
        form.Categories.Add(new Category() { ID = 3, CategoryName = "Category3" });
        form.Categories.Add(new Category() { ID = 4, CategoryName = "Category4" });
    
        // pass the CategoryForm view model
        return View(form);
    }
    
    [HttpPost]
    public ActionResult Save(CategoryForm cat)
    {
        // the view model will now have the correct categories
        List<Category> cl = new List<Category>(cat.Categories);
    
        return View("Index", cat);
    }