MVC Razor View: How to render a list of text boxes

2020-04-11 17:48发布

I have a List<Task> in my model. This list contains 2 tasks(say) in it

public List<Task> Tasks { get; set; }

public class Task    {
    public Task()
    {
        Title="";
        Total= 0;
    }

    public string Title{ get; set; }

    public int Total{ get; set; }
}

Now in my razor view, I want render 2 text boxes for each of the Tasks in the List<Tasks> of my model.

I didn't loop, just placed direct text boxes like:

@Html.TextBoxFor(m => m.Tasks[0].Title, new { @maxlength = "50"})
@Html.TextBoxFor(m => m.Tasks[0].Total, new { @maxlength = "2"})
<br>
@Html.TextBoxFor(m => m.Tasks[1].Title, new { @maxlength = "50"})
@Html.TextBoxFor(m => m.Tasks[1].Total, new { @maxlength = "2"})

This renders the form fine, but clicking the submit button doesn't do anything in FF. However it posts fine in IE9.

View source shows this html generated like this:

<input id="Tasks_0__Title" maxlength="50" name="Tasks[0].Title" type="text" value="" />
<input data-val="true" data-val-number="The field Total must be a number." data-val-required="The Total field is required." id="Tasks_0__Total" maxlength="2" name="Tasks[0].Total" type="text" value="" /> 

This HTML doesn't look right. It has name="Tasks[0].Total" which seems odd.

How should I do this so that I can access the text box values from List<> in my controller after post?

Thanks

EDIT: I just kept one row for test. This is the html I see in FF.

<input id="Tasks_0__Title" type="text" value="" name="Tasks[0].Title" maxlength="50">
<input id="Tasks_0__Total" type="text" value="" name="Tasks[0].Total" maxlength="2" data-val-required="The Total field is required." data-val-number="The field Total must be a number." data-val="true">

This doesn't post when I click the submit button.

Now if I change name="Tasks[0].Title" to name="Tasks_0__Title" and name="Tasks_0__Total" in FIREBUG it posts fine.

If I delete name completely it also posts fine in FF

2条回答
做自己的国王
2楼-- · 2020-04-11 18:06

In case you want to have multiple elements with textboxes.

 for (int i = 0; i < Model.Students.Count; i++)
                            {
                                @Html.HiddenFor(modelIem => Model.Students[i].StudentId)

                                <tr>
                                    <td>
                                        @Html.DisplayFor(modelItem => Model.Students[i].Student.FirstNames)
                                    </td>
                                    <td>
                                        @Html.DisplayFor(modelItem => Model.Students[i].Student.LastNames)
                                    </td>
                                    <td>
                                        @Html.TextBoxFor(modelItem => Model.Students[i].Score, new { @type = "number" })
                                    </td>
</tr> }

This is the ViewModel

public class MyModel 
{
 public List<StudentGrade> Students { get; set; }
    }

    public class StudentGrade {
        public ApplicationUser Student { get; set; }
        [Range(1, 100)]
        public int? Score { get; set; } = null;
        public string Description { get; set; } = null;
        public string StudentId { get; set; }
    }

At the End it will look like this. enter image description here

查看更多
放荡不羁爱自由
3楼-- · 2020-04-11 18:07

You should use Tasks[0].Total and Tasks[1].Total instead of Items:

@Html.TextBoxFor(m => m.Tasks[0].Title, new { @maxlength = "50"})
@Html.TextBoxFor(m => m.Tasks[0].Total, new { @maxlength = "2"})
<br/>
@Html.TextBoxFor(m => m.Tasks[1].Title, new { @maxlength = "50"})
@Html.TextBoxFor(m => m.Tasks[1].Total, new { @maxlength = "2"})

name="Tasks[0].Total" is not odd. That's exactly how the input should be named in order for the model binder to fetch the value back in the POST action. See this blog post for the wire format used by the default model binder when dealing with lists and dictionaries.

This being said I would recommend you using editor templates => instead of writing those 5 lines of code in your view replace them with:

@Html.EditorFor(x => x.Tasks)

and then inside the corresponding editor template (~/View/Shared/EditorTemplates/Task.cshtml) which will be automatically rendered for each element in the Tasks collection:

@model Task
@Html.TextBoxFor(m => m.Title, new { @maxlength = "50"})
@Html.TextBoxFor(m => m.Total, new { @maxlength = "2"})    
<br/>

Now you can leave the editor templates worry about proper naming convention, etc...

As far as your POST action is concerned:

[HttpPost]
public ActionResult Foo(MyViewModel model)
{
    // model.Tasks should be correctly bound here
    ...
}
查看更多
登录 后发表回答