验证同一部分视图的多个实例

4
我有一个场景,希望向用户展示数据并允许他们修改。为此,我有一个预设了当前对象值的表单。这个表单被包含在一个部分视图中,根据我的父对象有多少子对象而被调用循环。
同时,我还提供了一个空表单,以便用户创建这个子项的新实例。简而言之,我的形式如下:
编辑表单(一个部分视图):
@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。
为了解决这个问题,我覆盖了ID和名称的属性,但没有成功。
基本上,我只想在适当的表单旁边显示错误消息。我对这种方法还不是很熟悉,所以如果有不正确的地方,请告诉我。我的目标是允许用户在特定屏幕上查看/编辑和创建数据。
谢谢!
2个回答

0

不要自己生成nameid属性的值。如果您使用以下任何一种方式,MVC可以轻松解决:

  1. 编辑器模板 或者
  2. 在您的局部视图中传入HtmlFieldPrefix有关更多详细信息,请参见此博客

我将向您展示的解决方案是用于编辑器模板。

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
         https://dev59.com/oXM_5IYBdhLWcg3wlELO
    }

    [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>

由于我已经解决了大部分问题,所以我无法测试您的方法。话虽如此,我认为将您的答案留在这里是一个好主意,这样它可以帮助未来遇到这个问题的任何人。 - npinti

0

我最终成功地解决了这个问题。在看到this之前的SO问题后,我选择使用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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接