验证相同局部视图的多个实例

时间:2014-04-07 11:35:50

标签: c# asp.net-mvc asp.net-mvc-4 razor

我有一个场景,我想向用户显示数据并允许他们修改它。为此,我有一个预设当前对象值的表单。这个表单被封装为一个局部视图,根据我的父对象有多少个子对象,这个视图依次被调用。

我还在最后提供了一个空表单,以允许用户创建这个子项的新实例,所以简而言之我有这样的事情:

编辑表格(部分视图):

@model WebPortal.Models.AddressModel

@using (Html.BeginForm("EditAddress", "MerchantDetailsEditor", FormMethod.Post))
{
    @Html.ValidationSummary(true)

    @Html.LabelFor(model => model.StreetNumber, new { id = Model.ID + "_streetnumber_label", Name = Model.ID + "_streetnumber_label", @for = Model.ID + "_streetnumber" })
    @Html.TextBoxFor(address => address.StreetNumber, new { id = Model.ID + "_streetnumber", Name = Model.ID + "_streetnumber"})
    @Html.ValidationMessageFor(address => address.StreetNumber)

创建表单(另一部分视图)

@model WebPortal.Models.AddressModel
@{
    int counter = 1;
}

@using (Html.BeginForm("AddAddress", "MerchantDetailsEditor", FormMethod.Post))
{
    @Html.ValidationSummary(true)

    @Html.LabelFor(model => model.StreetNumber, new { id = counter + "_streetnumber_label", Name = counter + "_streetnumber_label", @for = counter + "_streetnumber" })
    @Html.TextBoxFor(address => address.StreetNumber, new { id = counter + "_streetnumber", Name = counter + "_streetnumber"})
    @Html.ValidationMessageFor(address => address.StreetNumber)
    counter++;

反过来这样称为:

@foreach (WebPortal.Models.AddressModel address in @Model.Addresses)
{
    @Html.Partial("Edit_Form", address)
}
@Html.Partial("Add_Form", new WebPortal.Models.AddressModel())

我面临的问题是,由于它们都共享相同的模型,因此验证错误正在应用并显示在我显示的所有实例旁边。所以,如果我在哪里编辑一个对象,我会得到错误,因为另一个表单(添加表单)是空的。

我已经读过,这是因为MVC在视图级别应用了组件ID,所以由于我使用的是同一视图的多个实例,因此它们都获得了相同的ID。

为了解决这个问题,我覆盖了IDName的属性,但无济于事。

基本上,我只是希望错误消息显示在相应的表单旁边。我对这种方法相当环保,所以如果不正确请告诉我。我的目标是允许用户在特定屏幕上查看/编辑和创建数据。

谢谢!

2 个答案:

答案 0 :(得分:0)

请勿自行生成nameid属性的值。如果你使用:

,MVC可以做到这一点
  1. 编辑模板或
  2. HtmlFieldPrefix传入您的部分观看次数See this blog for more details
  3. 我要向您展示的解决方案是编辑模板。

    AddressModel.cshtml(编辑器模板)

    @model WebPortal.Models.AddressModel
    
    @Html.LabelFor(model => model.StreetNumber)
    @Html.TextBoxFor(address => address.StreetNumber)
    @Html.ValidationMessageFor(address => address.StreetNumber)
    

    视图

    @for (int i = 0; i < Model.Addresses.Count; i++)
    {
        using (Html.BeginForm("EditAddress", "MerchantDetailsEditor"))
        {
            @Html.ValidationSummary(true)    
            <input type="hidden" name="Addresses.Index" value="@i" /> //http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
            @Html.EditorFor(m => m.Addresses[i]) //uses AddressModel.cshtml
            <input type="submit" value="Edit" />
        }
    }
    
    @using (Html.BeginForm("AddAddress", "MerchantDetailsEditor"))
    {
        @Html.ValidationSummary(true)
        @Html.EditorFor(m => m.AddAddress)
        <input type="submit" value="Add" />
    }
    

    控制器

        [HttpPost]
        public ActionResult EditAddress([Bind(Prefix="Addresses")] AddressModel[] i)
        {
             //learn more about BindAttribute here
             http://stackoverflow.com/questions/1317523/how-to-use-bind-prefix
        }
    
        [HttpPost]
        public ActionResult AddAddress([Bind(Prefix = "AddAddress")] AddressModel i)
        {
    
        }
    

    生成的HTML

    请注意,nameid属性具有唯一的数字索引,在此示例中为0

    <form action="/Home/EditAddress" method="post">
      <input type="hidden" name="Addresses.Index" value="0" />
      <label for="Addresses_0__StreetNumber">Street Number</label>
      <input data-val="true" data-val-required="The Street Number field is required." id="Addresses_0__StreetNumber" name="Addresses[0].StreetNumber" type="text" value="" />
      <span class="field-validation-valid" data-valmsg-for="Addresses[0].StreetNumber" data-valmsg-replace="true"></span>
      <input type="submit" value="Edit" />
    </form>
    

答案 1 :(得分:0)

我最终设法让这个工作。在遇到this之前的问题后,我选择使用Actions。所以代码现在看起来像这样:

编辑表格(仍然是部分观点)

@model WebPortal.Models.AddressModel


@using (Html.BeginForm("EditAddress", "MerchantDetailsEditor", FormMethod.Post))
{
    @Html.ValidationSummary(true)

    @Html.HiddenFor(model => model.ID)

    @Html.LabelFor(model => model.StreetNumber, new { id = Model.ID + "_streetnumber_label" })
    @Html.TextBoxFor(address => address.StreetNumber, new { id = Model.ID + "_streetnumber" })
    @Html.ValidationMessageFor(address => address.StreetNumber)
 ...

创建表单(仍然是部分视图)

@model WebPortal.Models.AddressModel
@{
    int counter = 1;
}

@using (Html.BeginForm("AddAddress", "MerchantDetailsEditor", FormMethod.Post))
{
    @Html.ValidationSummary(true)

    @Html.LabelFor(model => model.StreetNumber, new { id = counter + "_streetnumber_label" })
    @Html.TextBoxFor(address => address.StreetNumber, new { id = counter + "_streetnumber"})
    @Html.ValidationMessageFor(address => address.StreetNumber)
 ...

主要变化是我接下来做的事情:

<h2>Addresses</h2>
@foreach (WebPortal.Models.AddressModel address in @Model.Addresses)
{
    @Html.Action("_AddressEdit", address)
}

@if (ViewBag.AddressToAdd != null)
{
    @Html.Action("_AddressAdd", (WebPortal.Models.AddressModel)ViewBag.AddressToAdd)
}
else
{
    @Html.Action("_AddressAddNew")
}

我基本上为部分视图的每个实例提供了一个动作。这使我能够解决我的答案中链接中暴露的问题。

我的控制器现在看起来像这样:

 #region Actions
    public ActionResult _AddressEdit(AddressModel addressModel)
    {
        return View("...", addressModel);
    }

    public ActionResult _AddressAdd(AddressModel addressModel)
    {
        return View("...", addressModel);
    }

    public ActionResult _AddressAddNew()
    {
        return View("...");
    }
    #endregion Actions