【问题标题】:How to post a ViewModel from a list of ViewModels如何从 ViewModel 列表中发布 ViewModel
【发布时间】:2016-01-04 02:26:28
【问题描述】:

我目前有一个视图,我将视图模型列表传递给。然后我想选择其中一个 ViewModel 并将其发布回我的控制器。我目前有一个 foreach 循环遍历每个 ViewModel 并显示它们的数据。每个 ViewModel 都有一个提交按钮,但它似乎没有像我希望的那样工作。

解释的代码(查看):

@model IEnumerable<RoboticsScheduler.Models.UserVM>
...
@using (Html.BeginForm("UserApproved", "Approve"))
{
...
    @foreach (var item in Model)
    {
    ...
    @*Modified model data*@
    ...
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Approve User" class="btn btn-default" />
        </div>
    </div>
    }
}

然后在我的接受控制器中,我将 UserVM 作为参数,但我不断在控制器中获得空的 UserVM。

控制器中的代码:

public async Tas<ActionResult> UserApproved(UserVM uvm)
{
...
}

如果有人对我如何完成这样的事情有建议,那就太好了!

编辑

显示更深入的视图并将 BeginForm 移动到循环中:

...
@foreach (var item in Model)
{
    using (Html.BeginForm("Index", "Approved", FormMethod.Post))
    {
        <tr>
            <td>
                @Html.HiddenFor(modelItem => item.Id)
                @Html.DisplayFor(modelItem => item.Email)
            </td>
            <td>
                @item.EmailConfirmed.ToString()
            </td>
            <td>
                <span class="col-md-10">
                    @{List<string> roles = ViewBag.Roles;
                    int i = 0;
                    }
                    @foreach (string role in roles)
                    {
                        @Html.EditorFor(modelItem => item.Roles[i])
                        @Html.Label(role, new { @class = "control-label" })
                        <br />
                    }
                </span>
            </td>
            <td>
                <div class="form-group">
                    <div class="col-md-offset-2 col-md-10">
                        <input type="submit" value="Approve User" class="btn btn-default" />
                    </div>
                </div>
            </td>
        </tr>
    }
}

【问题讨论】:

  • 您需要显示视图 - 您如何为每个 UserVM 生成控件?如果它没有约束它是因为你的观点是错误的
  • 视图不好。除非您在没有任何 html 帮助程序的情况下手动创建了 html,否则 foreach 循环不可能生成正确的 name 属性。您需要显示视图。
  • @Jon,唯一重要的部分是你如何生成控件(模型的一两个属性只需要查看它们)
  • 是的,但答案取决于您在这里真正想做什么。您在一个表单中为集合中的每个项目生成控件,这表明您实际上想要回发整个集合。对吗?
  • 没什么区别 - 查看为隐藏输入和单选按钮生成的 html。它们的名称属性与您的模型无关,因此在您发布时永远不会绑定到您的模型。

标签: c# asp.net-mvc razor viewmodel model-binding


【解决方案1】:

解决方案是了解 MVC 在发布表单时如何将值绑定回控制器。详细解释一下。。

解决方案: 您的视图模型是@model IEnumerable&lt;RoboticsScheduler.Models.UserVM&gt;,因此表单提交变得很棘手,因为您处理单个对象。你有正确的 for 循环,但你需要了解它发生了什么。

@foreach (string role in roles)
 {
    @Html.EditorFor(modelItem => item.Roles[i])
    @Html.Label(role, new { @class = "control-label" })
    <br />
 }

这里的编辑器会渲染一个名称类似于item.Role 的 HTML 标记,这就是这里的问题。 name 参数是您需要正确设置表单帖子才能成功的唯一内容。所以你所要做的就是......

@Html.EditorFor(modelItem => item.Roles[i], new {Name = 'RoleType'}) 

在这里,我隐含地为输入标签命名。我还假设您的 ViewModel UserVM 有一个名为 RoleType 的属性。像下面的东西

public class UserVM
{
  public string RoleType {get;set;}
  public int UserId {get;set;} // you will see why this in my below explanation
} 

现在,当您提交表单时,MVC 将看到控制器操作的输入参数类型是什么,它的 UserVM 对象,现在它会在该对象内部查找与表单内的输入元素同名的属性。在我们的例子中,我们有一个名为 RoleType 的输入标签,我们在对象内部还有一个名为 RoleType 的属性,所以 MVC 很乐意绑定他的值。

建议: 在阅读了您的 cmets 之后,我了解到您想要显示用户想要从中选择角色的角色下拉列表。所以你可以试试这个..

更改从 Viewbag 中提取数据的方式的逻辑

@{
   List<string> roles = ViewBag.Roles;
   int i = 0;
   var RolesList = roles.select(c => new SelectListItem {Text=c,Value=c}).ToList();
 }

上面的代码将使用 Linq 语法构建一个 SelectListItem 对象列表(这是我们在 MVC 中处理下拉列表时必须使用的对象)。现在在里面 您的表单更改为此

@foreach (var item in Model)
   {
    using (Html.BeginForm("Index", "Approved", FormMethod.Post))
    {
        @Html.HiddenFor(modelItem => item.Id, new {Name = "UserId"}) 
        @Html.DropDownList("RoleType",RolesList)
        @Html.Label(role, new { @class = "control-label" })
        <br />
    }
   }

所以上面的语法说它是一个下拉列表,并且还有一个名称 RoleType ,还有一个名称更改为 UserId 的隐藏字段(我们知道为什么在我上面的解释中)。现在将在提交表单时发布。如果您希望它们也被发布,您可以将任何其他字段放在 hiddenfor 语法中。

注意:使用 MVC 时应该知道的主要一点是,默认的 MVC 模型绑定 完全依赖于 Name 属性输入标签(也是选择标签)以实现成功发布。

附加信息:上面的表单post等于在查询字符串中做同样的事情。。

yourlocalhost/Approved/Index?RoleType="Admin"&UserId="23"

您仍然可以使用相同的控制器语法。这是为了让您更清楚地了解 MVC 所做的模型绑定。您可以尝试并在 action 方法中检查您的对象,以查看填充到对象各自属性的值。

【讨论】:

  • 这是反 MVC 的,会产生各种问题。它不会提供真正的 2-way 模型绑定,如果需要返回视图,它将失败,客户端验证将不可能,它创建无效的 html 等(并且new { name = "UserId" } 不会更改名称属性,因此它不会绑定无论如何)
  • 不确定您在此声明中的意思It will fail if the view needs to be returned,验证我同意。只能使用 EditorFor 和数据注释。但我不同意new { name = "UserId" } 不起作用的事实,因为我在大多数情况下都可以使用它。如果我错了,请纠正我
  • 我也拒绝接受这是反 MVC,因为我所做的只是更改 name 属性,这对我来说很好。好的,这是一个场景。我们总是可以在 Razor 视图中编写纯 HTML 语法,对吧?我们可以让它和@Html.EditorFor.. 一样工作(最终渲染的DOM 是纯HTML)。因此更改名称属性或编写纯 HTML 都是一样的。一切都取决于开发人员的舒适区。
  • 您将一个对象发布到控制器,但是如果需要返回视图(比如由于服务器端验证错误),那么它将失败,因为视图需要一个集合(它会抛出异常)
  • 试试吧!它不会更改 name 属性(检查源代码以了解原因)。有一种解决方法(您几乎已经掌握了),但对于不了解 MVC 和模型绑定的内部工作原理的初学者来说太危险了。
【解决方案2】:
//Person
@model MvcApplication1.Models.Person
@{
     ViewBag.Title = "Index";    
 }

<h2>Person</h2>

@using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
    <div>
        @Html.TextBoxFor(model => model.Id, new { id = "Id"})
    </div>

    <div>
        @Html.TextBoxFor(model => model.Nome, new { id = "Name" })
    </div>

     if (Model != null && Model.Phone != null)
     {
         foreach (var phone in Model.Phone)
         {
             Html.RenderPartial("_Phone", phone);
         }
     }    

    <input type="submit" value="Save" />
}


//phone
@model MvcApplication1.Models.Phone

@using (Html.BeginCollectionItem("Phone"))
{
     <div>
        <div>
            @Html.EditorFor(model => model.PhoneNumber)
        </div>
    </div>
}

使用 Html.BeginCollectionItem https://www.nuget.org/packages/BeginCollectionItem/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多