MVC如何使用List Items POST动作方法传递对象列表

Jib*_*hew 5 razor asp.net-mvc-3

我想从Razor视图向控制器发布一个项目列表,但我得到一个对象列表为null我的类structre是

模型:

List<Subjects> modelItem

class Subjects
{
    int SubId{get;set;}
    string Name{get;set;}
    List<Students> StudentEntires{get;set;}
}

class StudentEntires
{
    int StudId{get;set;}
    string Name{get;set;}
    int Mark{get;set;}
}
Run Code Online (Sandbox Code Playgroud)

模型本身是一个项目列表,每个项目也包含子项目列表.示例模型是主题列表,每个主题包含学生列表,我想为每个学生输入标记

我的观点就像

@model IList<Subjects>  
@{   
   Layout = "~/Views/Shared/_Layout.cshtml";
}
@using (Html.BeginForm())
{    
   @Html.ValidationSummary(true)
   if (Model.Count > 0)
   {
       @for (int item = 0; item < Model.Count(); item++)
       {
           <b>@Model[item].Name</b><br />
           @foreach (StudentEntires markItem in Model[item].StudentEntires)
           {
               @Html.TextBoxFor(modelItem => markItem.Mark)
           }
       }
       <p style="text-align:center">
           <input type="submit" class="btn btn-primary" value="Update" />
       </p>
    }
}
Run Code Online (Sandbox Code Playgroud)

在控制器中

    [HttpPost]
    public ActionResult OptionalMarks(int Id,ICollection<Subjects> model)
    {
        //BUt my model is null. Any idea about this?
    }
Run Code Online (Sandbox Code Playgroud)

Joh*_*n H 22

你发现这很困难,因为你没有充分利用MVC框架的全部功能,所以请允许我提供一个有效的例子.

首先,让我们创建一个视图模型来封装视图的数据要求:

public class SubjectGradesViewModel
{
    public SubjectGradesViewModel()
    {
        Subjects = new List<Subject>();
    }

    public List<Subject> Subjects { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

接下来,创建一个表示主题模型的类:

public class Subject
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Student> StudentEntries { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

最后,一个代表学生的班级:

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Grade { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

此时,您拥有表示数据所需的所有类.现在让我们创建两个控制器动作,包括一些示例数据,以便您可以看到它是如何工作的:

public ActionResult Index()
{
    var model = new SubjectGradesViewModel();

    // This sample data would normally be fetched
    // from your database
    var compsci = new Subject
    {
        Id = 1,
        Name = "Computer Science",
        StudentEntries = new List<Student>()
        {
            new Student { Id = 1, Name = "CompSci 1" },
            new Student { Id = 2, Name = "CompSci 2" },
        }
    };

    var maths = new Subject
    {
        Id = 2,
        Name = "Mathematics",
        StudentEntries = new List<Student>()
        {
            new Student { Id = 3, Name = "Maths 1" },
            new Student { Id = 4, Name = "Maths 2" },
        }
    };

    model.Subjects.Add(compsci);
    model.Subjects.Add(maths);

    return View(model);
}

[HttpPost]
public ActionResult Index(SubjectGradesViewModel model)
{
    if (ModelState.IsValid)
    {
        return RedirectToAction("Success");
    }

    // There were validation errors
    // so redisplay the form
    return View(model);
}
Run Code Online (Sandbox Code Playgroud)

现在是构建视图的时候了,这部分在将数据发送回控制器时尤为重要.首先是Index视图:

@model SubjectGradesViewModel

@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)

    @Html.EditorFor(m => m.Subjects) <br />
    <input type="submit" />
}
Run Code Online (Sandbox Code Playgroud)

你会注意到我只是在使用Html.EditorFor,同时Subjects作为参数传递.我这样做的原因是因为我们要创建一个EditorTemplate代表一个Subject.我稍后会解释.现在,只知道它EditorTemplates并且DisplayTemplates是MVC中的特殊文件夹名称,因此它们的名称和位置很重要.

我们实际上要创建两个模板:一个用于Subject,一个用于Student.为此,请按照下列步骤操作:

  1. EditorTemplates在视图的当前文件夹中创建一个文件夹(例如,如果您的视图是Home\Index.cshtml,则创建该文件夹Home\EditorTemplates).
  2. 创建与模型相匹配的名称目录中的一个强类型的视图(即在这种情况下,你会作出两种观点,这将被称为Subject.cshtmlStudent.cshtml分别,(同样,命名很重要)).

Subject.cshtml 应该是这样的:

@model Subject

<b>@Model.Name</b><br />

@Html.HiddenFor(m => m.Id)
@Html.HiddenFor(m => m.Name)
@Html.EditorFor(m => m.StudentEntries)
Run Code Online (Sandbox Code Playgroud)

Student.cshtml 应该是这样的:

@model Student

@Html.HiddenFor(m => m.Id)
@Html.HiddenFor(m => m.Name)
@Html.DisplayFor(m => m.Name): @Html.EditorFor(m => m.Grade)
<br />
Run Code Online (Sandbox Code Playgroud)

而已.如果您现在构建并运行此应用程序,在POST索引操作上放置断点,您将看到模型已正确填充.

那么,它们EditorTemplates和它们的同行是DisplayTemplates什么?它们允许您创建可重复使用的视图部分,使您可以更多地组织视图.

关于他们的伟大的事情是模板助手,即Html.EditorForHtml.DisplayFor,有足够的智慧,当他们正在处理一个集合的模板就知道了.这意味着您不再需要遍历项目,每次手动调用模板.您也不必执行任何操作nullCount()检查,因为帮助程序将为您处理所有操作.你留下了干净,没有逻辑的观点.

EditorTemplates当您想要将集合POST到控制器操作时,还会生成适当的名称.这使得模型绑定到列表很多,比自己生成这些名称简单得多.有些时候你仍然需要这样做,但这不是其中之一.

  • 精彩的回答! (2认同)

Jas*_*ans 2

将操作方法​​签名更改为

public ActionResult OptionalMarks(ICollection<Subjects> model)

因为在你的 HTML 中,看起来没有任何命名的东西Id。但这不是你的主要问题。

接下来,使用 foo 循环执行以下操作

@for(int idx = 0; idx < Model[item].StudentEntires.Count();idx++)
{
    @Html.TextBoxFor(_ => Model[item].StudentEntries[idx])
}
Run Code Online (Sandbox Code Playgroud)

可能由于使用了循环foreachStudentEntries模型绑定器无法将所有内容拼凑在一起,因此返回 NULL。

编辑:

这是一个例子:

控制器

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var viewModel = new IndexViewModel();

        var subjects = new List<Subject>();
        var subject1 = new Subject();

        subject1.Name = "History";
        subject1.StudentEntires.Add(new Student { Mark = 50 });
        subjects.Add(subject1);

        viewModel.Subjects = subjects;

        return View(viewModel);
    }

    [HttpPost]
    public ActionResult Index(IndexViewModel viewModel)
    {
        return new EmptyResult();
    }
}
Run Code Online (Sandbox Code Playgroud)

看法

@model SOWorkbench.Controllers.IndexViewModel

@{
    ViewBag.Title = "Home Page";
}

@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)
    if (Model.Subjects.Any())
    {
        int subjectsCount = Model.Subjects.Count();
        for (int item = 0; item < subjectsCount; item++)
        {
            <b>@Model.Subjects[item].Name</b><br />            
            int studentEntriesCount = Model.Subjects[item].StudentEntires.Count();

            for(int idx = 0;idx < studentEntriesCount;idx++)
            {
                @Html.TextBoxFor(_ => Model.Subjects[item].StudentEntires[idx].Mark);
            }
        }
        <p style="text-align:center">
            <input type="submit" class="btn btn-primary" value="Update" />
        </p>
    }
}
Run Code Online (Sandbox Code Playgroud)

当您发布表单时,您应该看到数据返回到对象中viewModel