1

我已经阅读了一些关于 post-redirect-get 设计模式的内容,但我不确定它是否适合我的目的,因为我拥有的是一个 MVC 网站,它的设计看起来像一个应用程序,我在页面上有多个下拉菜单在我的控制器中全部绑定到一个整数数组,如下所示:

    [HttpPost]
    public ViewResult ResponseForm(PartyInvites.Models.GuestResponse response, int[] SelectedCustomer)
    {

       return View(response); // works but resets all my selected dropdowns
       // return View(); // gives an error that it can't rebind items in view

    }

我的观点:

@foreach (Schedule sched in Model.Schedules)
        {
@Html.DropDownList("MySelectedCustomer", new SelectList(sched.Customers, "Id", "FirstName"), "Select A Customer", new { @class = "SelectedCustomer" })

}

客人反应:

public class GuestResponse
    {
        [Required(ErrorMessage = "You must enter your name")]
        public string Name { get; set; }
        public string SomeString = "someString";
        public string Email { get; set; }
        public string Phone { get; set; }
        public bool? WillAttend { get; set; }
        public int SelectedSchedule = 0;
        public int SelectedCustomer = 0;

        public List<Schedule> Schedules
        {
            get
            {
                return new List<Schedule>() { new Schedule() { ScheduleName = "party1", ScheduleId = 1 }, new Schedule() { ScheduleId = 2, ScheduleName = "party2" } };
            }
            set
            {
                Schedules = value;
            }
        }
    }

SelectCustomer 属性是 GuestResponse 类的属性。所有的下拉列表都是绑定的,如果我更改了一些,它们会很好地绑定到 int[] SelectedCustomer 集合。但是我想返回我的视图(所以它本质上什么都不做)但这会将所有下拉列表重置为其原始状态,因为响应从未完全绑定,因为有多个下拉列表并且 MVC 无法对其进行模型绑定。这样做的最佳方法是什么,可以这么说来保持状态?

4

2 回答 2

2

处理此问题的正确方法是使用视图模型,而不是将域模型传递给视图。

但是,如果您不想遵循良好的做法,您可以生成这样的下拉列表作为解决方法:

for (int i = 0; i < Model.Schedules.Count; i++)
{
    @Html.DropDownList(
        "MySelectedCustomer[" + i + "]", 
        new SelectList(
            Model.Schedules[i].Customers, 
            "Id", 
            "FirstName", 
            Request["MySelectedCustomer[" + i + "]"]
        ), 
        "Select A Customer", 
        new { @class = "SelectedCustomer" }
    )
}

正确的方法是int[] SelectedCustomers在你的视图模型上有一个类型的属性,并使用 DropDownListFor 帮助器的强类型版本:

for (int i = 0; i < Model.Schedules.Count; i++)
{
    @Html.DropDownListFor(
        x => x.SelectedCustomers, 
        Model.Schedules[i].AvailableCustomers, 
        "Select A Customer", 
        new { @class = "SelectedCustomer" }
    )
}

并且您的 POST 控制器操作显然会将您定义为参数的视图模型:

[HttpPost]
public ViewResult ResponseForm(GuestResponseViewModel model)
{
    // The model.SelectedCustomers collection will contain the ids of the selected
    // customers in the dropdowns

    return View(model);
}

而且由于您提到了 Redirect-After-Post 设计模式,这确实是要使用的正确模式。如果成功,您应该重定向到 GET 操作:

[HttpPost]
public ViewResult ResponseForm(GuestResponseViewModel model)
{
    if (!ModelState.IsValid)
    {
        // the model is invalid => redisplay the view so that the user can fix
        // the errors
        return View(model);
    }

    // at this stage the model is valid => you could update your database with the selected
    // values and redirect to some other controller action which in turn will fetch the values
    // from the database and correctly rebind the model
    GuestResponse domainModel = Mapper.Map<GuestResponseViewModel, GuestResponse>(model);
    repository.Update(domainModel);

    return RedirectToAction("Index");
}
于 2013-03-31T19:16:36.600 回答
0

注意:我首先要解决为什么它没有绑定任何东西,但这并没有解决数组问题,我稍后会谈到。大多数人在使用 MVC 时出错的地方在于他们没有利用 MVC 的内置特性来处理这些情况。他们坚持做foreach的和手动渲染的东西,但没有考虑到集合状态。

重置值的原因是因为您使用的是Html.DropDownList()而不是Html.DropDownListFor(),并且您将发布的属性名称重命名为与模型属性名称不同的名称。

您可以简单地将其更改为:

@Html.DropDownList("SelectedCustomer", // note the removal of "My"
    new SelectList(sched.Customers, "Id", "FirstName"), 
    "Select A Customer", new { @class = "SelectedCustomer" })

但是,如果您刚刚使用强类型版本,您就不会遇到这个问题,并且不会让您非常头疼。

@Html.DropDownListFor(x => x.SelectedCustomer, 
    new SelectList(sched.Customers, "Id", "FirstName"), 
    "Select A Customer", new { @class = "SelectedCustomer" })

至于数组,您应该使用一个 EditorTemplate 作为 Schedules,并且在该 EditorTemplate 中您只需创建您的 html,就好像它是一个单独的项目一样。Editor/DisplayTemplates 的优点在于它们会自动处理集合。

在 Views/Controller 文件夹中创建一个名为 EditorTemplates 的文件夹。在该文件夹中,创建一个名为 Schedule.cshtml 的空文件(假设 Schedules 是一个 List 或 Schedule 数组)。在那里,您有代码来呈现单个计划。

编辑:

达林提出了一个很好的观点。我将对模型进行一些小改动,并将 Selected 属性添加到 Schedule 和 GuestResponse,然后您可以使用 Linq 返回选定的计划,这会简化事情。

编辑2:

您描述的问题与您显示的代码之间存在一些冲突。我建议你弄清楚你想要做什么,因为你的代码并没有真正反映一个可行的模型。

于 2013-03-31T19:24:43.520 回答