Return a List from a view in view model

2019-01-20 20:01发布

问题:

This is my situation:

I have this view model:

public class ViewModel
{
   public DateTime someDate { get; set; }
   public String someString { get; set; }
   public List<E> someList { get; set; }
}

What I have to do is in the view set the date, write some text and then select from a list of E any number of E. The ViewModel returned in the action must have the date, the text and contain the list of selected items.

What I need to know is how to handle said list. How can I add each selected item to the models' List. I was thinking on adding a property public bool selected to E and then send all items and filter the selected ones on the server, however I would rather not send back and forth all that data since the list can be quite large.

I am using MVC3 with razor and JQUERY AJAX for all my form posts.

If I am not making myself clear, please let me know.

Thank you.

回答1:

Here's one technique that you could use to achieve this.

Let's start with the view model:

public class ViewModel
{
    public DateTime SomeDate { get; set; }
    public string SomeString { get; set; }
    public List<E> SomeList { get; set; }
}

public class E
{
    public bool Selected { get; set; }
    public string Foo { get; set; }
    public string Bar { get; set; }
}

then we write some controller to handle the rendering of the view and the AJAX request:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new ViewModel
        {
            SomeDate = DateTime.Now,
            SomeString = "some text",
            SomeList = Enumerable.Range(1, 7).Select(x => new E
            {
                Foo = "foo " + x,
                Bar = "bar " + x
            }).ToList()
        };
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(ViewModel model)
    {
        // Here we will get our view model properly bound and 
        // the list will contain only the items that the user
        // has selected (see below...)

        // TODO: do some processing

        return Content("Thanks for submitting this data", "text/plain");
    }
}

then we move on to the ~/Views/Home/Index.cshtml view:

@model ViewModel

@using (Html.BeginForm()) 
{
    <div>
        @Html.LabelFor(x => x.SomeDate)
        @Html.EditorFor(x => x.SomeDate)
    </div>

    <div>
        @Html.LabelFor(x => x.SomeString)
        @Html.EditorFor(x => x.SomeString)
    </div>

    <table>
        <thead>
            <tr>
                <th></th>
                <th>Foo</th>
                <th>Bar</th>
            </tr>
        </thead>
        <tbody>
            @Html.EditorFor(x => x.SomeList)
        </tbody>
    </table>

    <input type="submit" value="Send selected values to server using AJAX" />
}

and finally we define an Editor template for the E type (~/Views/Home/EditorTemplates/E.cshtml) which will be rendered for each element of the collection:

@{
    var index = Guid.NewGuid().ToString();
    var prefix = Regex.Replace(ViewData.TemplateInfo.HtmlFieldPrefix, @"\[\d+\]$", match =>
    {
        return string.Format("[{0}]", index);
    });
    ViewData.TemplateInfo.HtmlFieldPrefix = prefix;
}
<input type="hidden" name="SomeList.Index" value="@index" />
<tr>
    <td>
        @Html.DisplayFor(x => x.Foo)
        @Html.HiddenFor(x => x.Foo)
    </td>
    <td>
        @Html.DisplayFor(x => x.Bar)
        @Html.HiddenFor(x => x.Bar)
    </td>
    <td>
        @Html.CheckBoxFor(x => x.Selected)
    </td>
</tr>

OK, so at this stage we haven't yet written the javascript part so this should behave as a normal HTML form and when it is submitted it will send all the values to the server.

And final piece would be to AJAXify the form and POST only the records that the user selected in the request. So we could do this in a separate javascript file:

$(function () {
    $('form').submit(function () {
        // we clone the original form and we will
        // filter out the non-selected fields
        var myForm = $(this).clone(false, false);

        $('tr', myForm).each(function () {
            var isSelected = $(':checkbox', this).is(':checked');
            if (!isSelected) {
                $(this).remove();
            }
        });

        $.ajax({
            url: this.action,
            type: this.method,
            data: myForm.serialize(),
            success: function (result) {
                alert(result);
            }
        });

        return false;
    });
});

As a good article for handling dynamic lists I would recommend you the following blog post.