Sending/POSTing collection data from an HTML/Razor form to an MVC action argument model

前端 未结 1 1410
眼角桃花
眼角桃花 2021-01-21 13:08

I am working on a project to help students and advisers select the best courses for the next semester, using ASP.NET MVC 5. The first step is for the student to select the cours

相关标签:
1条回答
  • 2021-01-21 13:33

    I believe there is an app for that called the BeginCollectionItem HtmlHelper. It is discussed briefly with references here, and is based on a blog article written by Steve Sanderson a few years ago.

    The problem is that model binding with form collections is not quite the same as model binding with scalar inputs in MVC. Your collection needs an indexer, as discussed here. If it is not binding as expected, examine the name attributes of the form inputs that get rendered, and compare them to the property names and structures within the post model (action argument) class.

    From the looks of it, HTML output that looked more like this should cause the action argument to be not null:

    <tr>
        <td>HTTP 101</td>
        <td>HTP-101</td>
        <td>HTP-101</td>
        <td><input type="checkbox" name="PossibleCourses[0].Selected" /></td>
        <td><input type="text" name="PossibleCourses[0].Semester"></td>
        <td><input type="text" name="PossibleCourses[0].Grade"></td>
    </tr>
    <tr>
        <td>MVC 101</td>
        <td>MVC-101</td>
        <td>MVC-101</td>
        <td><input type="checkbox" name="PossibleCourses[1].Selected" /></td>
        <td><input type="text" name="PossibleCourses[1].Semester"></td>
        <td><input type="text" name="PossibleCourses[1].Grade"></td>
    </tr>
    

    ...and so on, which the following razor should output:

    @for (var i = 0; i <= Model.PossibleCourses.Count; i++)
    {
        var course = Model.PossibleCourses[i];
        <tr>
            <td>@course.BaseCourse.Name</td>
            <td>@course.BaseCourse.CourseNumber</td>
            <td>@course.BaseCourse.CourseNumber</td>
            <td>@Html.CheckBox(string.Format("PossibleCourses[{0}].Selected", i),
                course.Selected)</td>
            <td>@Html.TextBox(string.Format("PossibleCourses[{0}].Semester", i),
                course.Semester)</td>
            <td>@Html.TextBox(string.Format("PossibleCourses[{0}].Grade", i),
                course.Grade)</td>
        </tr>
    }
    

    Note how the name attributes of the form input elements correspond to the name of the indexable (List<Course>) property in your action argument Model, and the indexed (Course) property names wrapped inside the collection. This is one way to help the model binder figure out how to populate the action argument class instance with data, by making the input name attributes match the method argument property names.

    You could also use a GUID (or any string for that matter) to serve as an indexer, which is what BeginItemCollection does internally. The following should also help the model binder be able to populate the action argument so that it does not come in as null to the action method:

    @foreach (var course in Model.PossibleCourses)
    {
        var indexer = Guid.NewGuid(); // or possibly course.CourseId
        <tr>
            <td>@course.BaseCourse.Name</td>
            <td>@course.BaseCourse.CourseNumber</td>
            <td>@course.BaseCourse.CourseNumber</td>
            <td>@Html.Hidden("PossibleCourses.index", indexer)
                @Html.CheckBox(string.Format("PossibleCourses[{0}].Selected", indexer),
                course.Selected)</td>
            <td>@Html.TextBox(string.Format("PossibleCourses[{0}].Semester", indexer),
                course.Semester)</td>
            <td>@Html.TextBox(string.Format("PossibleCourses[{0}].Grade", indexer),
                course.Grade)</td>
        </tr>
    }
    

    All that matters is each group of form elements which correspond to a collection item in the action argument class must share the same indexer, and the indexer must be different from other groups of form elements that correspond to a different collection item in the action argument class. A sample of this solution can be understood by reading this question and its answer.

    0 讨论(0)
提交回复
热议问题