C# MVC 使用一个模型发送和检索列表<> to/from 视图
C# MVC send and retrieve a list<> to/from a view using one model
我正在尝试编写一个页面,从 SQL table 中检索 List<> 并显示它,以便用户可以进行更改。我已经能够显示信息了。我 运行 遇到了提交问题。我无法找到一种方法来获取输入的数据并将更改发送到 List<>,以便我可以在 controller/model 中对其进行操作。
目前我在提交时将新数据放入数组中。因为我在视图中使用 @model List<modelname>
。是否可以替换值或可能将数据放入视图中的新列表?
你绝对可以做到这一点,但人们最初确实倾向于 运行 遇到问题,所以我将提供一个完整的例子。
首先,让我们定义一个 HomeController
,它有一个 Index
动作,returns 一个 List<EmployeeViewModel>
:
public class HomeController : Controller
{
public ActionResult Index()
{
// Assume this would really come from your database.
var employees = new List<EmployeeViewModel>()
{
new EmployeeViewModel { Id = 1, Name = "Employee 1" },
new EmployeeViewModel { Id = 2, Name = "Employee 2" },
new EmployeeViewModel { Id = 3, Name = "Employee 3" },
};
return View(employees);
}
[HttpPost]
public ActionResult Index(List<EmployeeViewModel> employees)
{
// Rest of action
}
}
人们首先解决这个问题的典型方法是在 index.cshtml
中做类似下面的事情:
@model List<EmployeeViewModel>
@using (Html.BeginForm())
{
foreach (var item in Model)
{
<div class="row">
@Html.EditorFor(x => item.Id)
</div>
<div class="row">
@Html.EditorFor(x => item.Name)
</div>
}
<input type="submit" />
}
乍一看,这似乎行得通。但是,如果您在 POST 操作中放置一个断点并单击提交按钮,您会注意到 employees
是 null
。这是因为 foreach
循环正在生成 HTML,如下所示:
<input name="item.Id" type="number" value="1" />
<input name="item.Name" type="text" value="Employee 1" />
<input name="item.Id" type="number" value="2" />
<input name="item.Name" type="text" value="Employee 2" />
<input name="item.Id" type="number" value="3" />
<input name="item.Name" type="text" value="Employee 3" />
我已经删除了一些不相关的部分,但请注意每个员工的 name
属性如何具有相同的值。当默认模型绑定器尝试在服务器上构建数据列表时,它必须能够区分不同的员工,并且因为它不能,它导致列表为 null
.
那么为什么它会生成相同的值?是因为:
@Html.EditorFor(x => item.Id)
@Html.EditorFor(x => item.Name)
我们没有将索引值传递给 HtmlHelper
方法调用,因此信息不会进入生成的 HTML。我们可以简单地通过使用 for
循环来解决这个问题:
for (int i = 0; i < Model.Count; i++)
{
@Html.EditorFor(x => Model[i].Id)
@Html.EditorFor(x => Model[i].Name)
}
由于我们现在为每个方法调用提供一个索引,因此生成的 HTML 现在确实包含每个员工的索引:
<input name="[0].Id" type="number" value="1" />
<input name="[0].Name" type="text" value="Employee 1" />
<input name="[1].Id" type="number" value="2" />
<input name="[1].Name" type="text" value="Employee 2" />
<input name="[2].Id" type="number" value="3" />
<input name="[2].Name" type="text" value="Employee 3" />
这允许默认模型绑定器将 Id
和 Name
与每个 EmployeeViewModel
相关联,从而使其能够在服务器上正确构造类型。
至此,您的问题已解决,但是,如果可以避免,不建议使用 for
循环,这会将我们带到编辑器模板。编辑器模板是 HtmlHelper
方法,允许我们为给定类型呈现自定义模板(即视图)。因此,让我向您展示如何使用上面的示例代码执行此操作的示例。
首先,您需要执行以下操作:
- 在
~/Views/Home/
文件夹中创建一个 EditorTemplates
文件夹(EditorTemplates
名称在 MVC 中具有特殊含义,因此正确拼写很重要)。
- 在该文件夹内创建一个
EmployeeViewModel.cshtml
视图(同样,名称在这里很重要:它应该与您希望为其创建自定义模板的类型的名称相匹配)。
完成后,它应该如下所示:
现在,打开 EmployeeViewModel.cshtml
,将 Index.cs
中的渲染代码放入其中:
@model EmployeeViewModel
<div class="row">
@Html.EditorFor(x => x.Id)
</div>
<div class="row">
@Html.EditorFor(x => x.Name)
</div>
最后,打开index.cshtml
,改成如下:
@model List<EmployeeViewModel>
@using (Html.BeginForm())
{
@Html.EditorFor(x => x)
<input type="submit" />
}
Html.EditorFor
和 Html.DisplayFor
都足够聪明,可以识别何时为集合调用它们,因此它们寻找自定义模板来呈现集合的类型(在本例中, EditorFor
将为 EmployeeViewModel
).
寻找编辑器模板
由于我们提供了一个编辑器模板,它不仅会为集合中的每个项目呈现它,还会为每个项目生成正确的索引,为模型绑定器提供它需要的所有信息在服务器上重建该集合。
最终结果是模型绑定变得更简单,不仅视图的代码更简单,而且还根据所涉及的类型进行了拆分,这使您的视图更易于使用,而不是无所不能的大视野。
还要提一下,因为我的例子是直接使用集合,仅此而已,你实际上可以用@Html.EditorForModel()
替换@Html.EditorFor(x => x)
。最初我没有这样做,因为我不想给人留下这样的印象,即仅使用后者即可调用模板。
我正在尝试编写一个页面,从 SQL table 中检索 List<> 并显示它,以便用户可以进行更改。我已经能够显示信息了。我 运行 遇到了提交问题。我无法找到一种方法来获取输入的数据并将更改发送到 List<>,以便我可以在 controller/model 中对其进行操作。
目前我在提交时将新数据放入数组中。因为我在视图中使用 @model List<modelname>
。是否可以替换值或可能将数据放入视图中的新列表?
你绝对可以做到这一点,但人们最初确实倾向于 运行 遇到问题,所以我将提供一个完整的例子。
首先,让我们定义一个 HomeController
,它有一个 Index
动作,returns 一个 List<EmployeeViewModel>
:
public class HomeController : Controller
{
public ActionResult Index()
{
// Assume this would really come from your database.
var employees = new List<EmployeeViewModel>()
{
new EmployeeViewModel { Id = 1, Name = "Employee 1" },
new EmployeeViewModel { Id = 2, Name = "Employee 2" },
new EmployeeViewModel { Id = 3, Name = "Employee 3" },
};
return View(employees);
}
[HttpPost]
public ActionResult Index(List<EmployeeViewModel> employees)
{
// Rest of action
}
}
人们首先解决这个问题的典型方法是在 index.cshtml
中做类似下面的事情:
@model List<EmployeeViewModel>
@using (Html.BeginForm())
{
foreach (var item in Model)
{
<div class="row">
@Html.EditorFor(x => item.Id)
</div>
<div class="row">
@Html.EditorFor(x => item.Name)
</div>
}
<input type="submit" />
}
乍一看,这似乎行得通。但是,如果您在 POST 操作中放置一个断点并单击提交按钮,您会注意到 employees
是 null
。这是因为 foreach
循环正在生成 HTML,如下所示:
<input name="item.Id" type="number" value="1" />
<input name="item.Name" type="text" value="Employee 1" />
<input name="item.Id" type="number" value="2" />
<input name="item.Name" type="text" value="Employee 2" />
<input name="item.Id" type="number" value="3" />
<input name="item.Name" type="text" value="Employee 3" />
我已经删除了一些不相关的部分,但请注意每个员工的 name
属性如何具有相同的值。当默认模型绑定器尝试在服务器上构建数据列表时,它必须能够区分不同的员工,并且因为它不能,它导致列表为 null
.
那么为什么它会生成相同的值?是因为:
@Html.EditorFor(x => item.Id)
@Html.EditorFor(x => item.Name)
我们没有将索引值传递给 HtmlHelper
方法调用,因此信息不会进入生成的 HTML。我们可以简单地通过使用 for
循环来解决这个问题:
for (int i = 0; i < Model.Count; i++)
{
@Html.EditorFor(x => Model[i].Id)
@Html.EditorFor(x => Model[i].Name)
}
由于我们现在为每个方法调用提供一个索引,因此生成的 HTML 现在确实包含每个员工的索引:
<input name="[0].Id" type="number" value="1" />
<input name="[0].Name" type="text" value="Employee 1" />
<input name="[1].Id" type="number" value="2" />
<input name="[1].Name" type="text" value="Employee 2" />
<input name="[2].Id" type="number" value="3" />
<input name="[2].Name" type="text" value="Employee 3" />
这允许默认模型绑定器将 Id
和 Name
与每个 EmployeeViewModel
相关联,从而使其能够在服务器上正确构造类型。
至此,您的问题已解决,但是,如果可以避免,不建议使用 for
循环,这会将我们带到编辑器模板。编辑器模板是 HtmlHelper
方法,允许我们为给定类型呈现自定义模板(即视图)。因此,让我向您展示如何使用上面的示例代码执行此操作的示例。
首先,您需要执行以下操作:
- 在
~/Views/Home/
文件夹中创建一个EditorTemplates
文件夹(EditorTemplates
名称在 MVC 中具有特殊含义,因此正确拼写很重要)。 - 在该文件夹内创建一个
EmployeeViewModel.cshtml
视图(同样,名称在这里很重要:它应该与您希望为其创建自定义模板的类型的名称相匹配)。
完成后,它应该如下所示:
现在,打开 EmployeeViewModel.cshtml
,将 Index.cs
中的渲染代码放入其中:
@model EmployeeViewModel
<div class="row">
@Html.EditorFor(x => x.Id)
</div>
<div class="row">
@Html.EditorFor(x => x.Name)
</div>
最后,打开index.cshtml
,改成如下:
@model List<EmployeeViewModel>
@using (Html.BeginForm())
{
@Html.EditorFor(x => x)
<input type="submit" />
}
Html.EditorFor
和 Html.DisplayFor
都足够聪明,可以识别何时为集合调用它们,因此它们寻找自定义模板来呈现集合的类型(在本例中, EditorFor
将为 EmployeeViewModel
).
由于我们提供了一个编辑器模板,它不仅会为集合中的每个项目呈现它,还会为每个项目生成正确的索引,为模型绑定器提供它需要的所有信息在服务器上重建该集合。
最终结果是模型绑定变得更简单,不仅视图的代码更简单,而且还根据所涉及的类型进行了拆分,这使您的视图更易于使用,而不是无所不能的大视野。
还要提一下,因为我的例子是直接使用集合,仅此而已,你实际上可以用@Html.EditorForModel()
替换@Html.EditorFor(x => x)
。最初我没有这样做,因为我不想给人留下这样的印象,即仅使用后者即可调用模板。