无法通过 Html.BeginForm() 将数据从视图传递到操作 [英] Failing to pass data from view to the Action by Html.BeginForm()

查看:20
本文介绍了无法通过 Html.BeginForm() 将数据从视图传递到操作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 asp.net mvc 的新手,所以我失败的原因也可能是基本的,但我现在工作了将近一天后似乎找不到它.

我想要做的是从索引视图中获取编辑后的模型并将其传递给没有视图的第二个操作并返回相关的返回 RedirectToAction("Index")控制器.在 OrdersItemsController 中,我的操作如下:

[HttpPost][验证AntiForgeryToken]public ActionResult MarkedShipped(IEnumerable orderItems){如果(模型状态.IsValid){foreach (var item in orderItems){unitOfWork.GenericRepository().Update(item);}}return RedirectToAction("索引");}

在视图中 OrdersItems 文件夹中的 Index.cshtml 中,我所做的如下:

@model IEnumerable@{ViewBag.Title = "索引";}<h2>索引</h2>@using (Html.BeginForm("MarkedShipped", "OrdersItems", new { orderItems = Model })){@Html.AntiForgeryToken()@Html.ValidationSummary(true)<table class="table"><tr><th>@Html.DisplayNameFor(model => model.quantity)</th><th>@Html.DisplayNameFor(model => model.itemprice)</th><th>@Html.DisplayNameFor(model =>model.trackingnumber)</th></tr>@foreach(模型中的变量项目){<tr><td>@Html.DisplayFor(modelItem => item.quantity)</td><td>@Html.DisplayFor(modelItem => item.itemprice)</td><td>@Html.EditorFor(modelItem => item.trackingnumber)</td><td>@Html.ActionLink("Edit", "Edit", new { id = item.itemid })</td></tr>}<div class="form-group"><div class="col-md-offset-2 col-md-10"><input type="submit" value="MarkShipped" class="btn btn-default"/>

}

我的问题是,我无法从带有 orderItems 参数的视图中获取模型,我不确定这是否是我想要完成的正确语法";但是当操作被调用时,我得到的 orderItems 是一个 orders_items 列表,其中 Count = 0 不是空值.

我也检查过是否有应用程序级别的异常,但在 Global.asax

中的 Application_Error 没有异常

我现在被困了一天,所以如果有人能指出我如何将模型(或编辑过的数据")传递给我更新数据库的操作,我将不胜感激.谢谢.

解决方案

首先,您不能将作为集合的模型(或包含作为复杂对象或集合的属性的模型)添加到表单路由参数.帮助程序在内部调用 .ToString() 方法,如果您检查为表单标记生成的 html,您将看到类似

的内容

并且绑定将失败,因为您的集合不能绑定到字符串.即使它确实有效,也没有意义,因为它只会回发模型的原始值.

接下来,您不能使用 foreach 循环来生成表单控件.同样,如果您检查生成的 html,您将看到 EditorFor() 方法正在生成具有重复 id 属性(无效 html)和重复 name 属性,这些属性在您发布时没有绑定到集合的必要索引器.您需要为 typeof orders_items

使用自定义 EditorTemplatefor 循环

使用 for 循环意味着你的模型必须实现 IList 并且视图需要是

@model IList@using (Html.BeginForm())//仅当控制器和动作与GET方法不同时才需要添加{....@for(int i = 0; i < Model.Count; i++){@Html.DisplayFor(m => m[i].quantity)....@Html.EditorFor(m => m[i].trackingnumber)}....}

使用EditorTemplate,在/Views/Shared/EditorTemplates/orders_items.cshtml 中创建一个partial(注意文件名必须与类名匹配)

@model Hochanda.CraftsHobbiesAndArts.DataAccess.orders_items<tr><td>@Html.DisplayFor(m => m.quantity)</td>....<td>@Html.EditorFor(m => m.trackingnumber)</td>....</tr>

然后在主视图中(你可以使用IEnumerable)

@model IEnumerable@using (Html.BeginForm()){<table class="table"><头>....<头>@Html.EditorFor(m => m)</tbody>....}

EditorFor() 方法接受 IEnumerable 并将根据 EditorTemplate 中的 html 为您集合中的每个项目生成一行代码>

在这两种情况下,如果您检查 html,您现在将看到模型在发布时绑定所需的正确名称属性

<input type="text" name="[1].trackingnumber" .../><input type="text" name="[3].trackingnumber" .../>

I am very new at asp.net mvc, so the reason behind my failure might be something basic as well, but I can't seem to find it after nearly a days work now.

What I am trying to do is to get the edited Model from the Index view and pass it to a second action which does not have view and returns return RedirectToAction("Index") in the related controller. In OrdersItemsController my Action is as the following:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult MarkedShipped(IEnumerable<orders_items> orderItems)
{
    if (ModelState.IsValid)
    {
        foreach (var item in orderItems)
        {
            unitOfWork.GenericRepository<orders_items>().Update(item);
        }
    }
    return RedirectToAction("Index");
}

And in the Index.cshtml which is in OrdersItems folder in the Views, what I did is as following:

@model IEnumerable<Project.DataAccess.orders_items>
@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

@using (Html.BeginForm("MarkedShipped", "OrdersItems", new { orderItems = Model }))
{

    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <table class="table">
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.quantity)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.itemprice)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.trackingnumber)
            </th>
        </tr>

        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.quantity)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.itemprice)
                </td>
                <td>
                    @Html.EditorFor(modelItem => item.trackingnumber)
                </td>
                <td>
                    @Html.ActionLink("Edit", "Edit", new { id = item.itemid })
                </td>
            </tr>

        }
    </table>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="MarkShipped" class="btn btn-default" />
        </div>
    </div>
}

My problem is, I am not able to get the Model from the view with orderItems parameter, I am not sure if this is the right "syntax" for what I am trying to accomplish; but what I get for orderItems when the action is called is a List of orders_items with Count = 0 not a null value.

I have also checked if there is an application level exception, but got no exception from Application_Error in Global.asax

I am stuck for a day now so If anyone can point me a direction on how to pass the Model (or "the edited data") to the action for me to update the db, I would be very grateful. Thanks.

解决方案

Firstly you cannot add a model which is a collection (or a model which contains a property which is a complex object or collection) to a forms route parameters. Internally the helper calls the .ToString() method and if you inspect the html generated for your form tag you will see something like

<form action=OrdersItems/MarkedShipped?orderItems=System.Collection.Generic.......

and binding will fail since you collection cannot be bound to a string. Even if it did work, it would be rather pointless because it would just post back the original values of the model.

Next, you cannot use a foreach loop to generate form controls. Again if you inspect the html your generating you will see that the EditorFor() method is generating inputs with duplicate id attributes (invalid html) and duplicate name attributes which do not have the necessary indexers to bind to a collection when you post. You need to use either a for loop of a custom EditorTemplate for typeof orders_items

Using a for loop means that your model must implement IList<T> and the view needs to be

@model IList<Hochanda.CraftsHobbiesAndArts.DataAccess.orders_items>
@using (Html.BeginForm()) // only necessary to add the controller and action if they differ from the GET method
{
  ....
  @for(int i = 0; i < Model.Count; i++)
  {
    @Html.DisplayFor(m => m[i].quantity)
    ....
    @Html.EditorFor(m => m[i].trackingnumber)
  }
  ....
}

Using an EditorTemplate, create a partial in /Views/Shared/EditorTemplates/orders_items.cshtml (note the name of the file must match the name of the class)

@model Hochanda.CraftsHobbiesAndArts.DataAccess.orders_items
<tr>
  <td>@Html.DisplayFor(m => m.quantity)</td>
  ....
  <td>@Html.EditorFor(m => m.trackingnumber)</td>
  ....
</tr>

and then in the main view (you can use IEnumerable<T>)

@model IEnumerable<Hochanda.CraftsHobbiesAndArts.DataAccess.orders_items>
@using (Html.BeginForm())
{
  <table class="table">
    <thead>
      ....
    <thead>
    <tbody>
      @Html.EditorFor(m => m)
    </tbody>
  </table>
  ....
}

The EditorFor() method accepts IEnumerable<T> and will generate one row for each item in you collection based on the html in the EditorTemplate

In both cases, it you inspect the html you will now see the correct name attributes necessary for you model to bind when you post

<input type="text" name="[0].trackingnumber" ... />
<input type="text" name="[1].trackingnumber" ... />
<input type="text" name="[3].trackingnumber" ... />

这篇关于无法通过 Html.BeginForm() 将数据从视图传递到操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆