每次使用新的Guid索引时,如何获取验证消息以呈现在集合属性上?

枪手2171

在此示例ASP.Net MVC 4程序中,我让用户填写有关赛马的详细信息。比赛的名称以及所涉及的赛马名单。每匹马都有一个名字和年龄。

该表单使用ajax和javascript允许用户即时添加和删除马输入字段,然后在按下提交按钮时立即全部提交。

为了使此过程更容易,我使用了Matt Lunn制作html helper

public static MvcHtmlString EditorForMany<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, IEnumerable<TValue>>> expression, string htmlFieldName = null) where TModel : class
{
    var items = expression.Compile()(html.ViewData.Model);
    var sb = new StringBuilder();

    if (String.IsNullOrEmpty(htmlFieldName))
    {
        var prefix = html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix;

        htmlFieldName = (prefix.Length > 0 ? (prefix + ".") : String.Empty) + ExpressionHelper.GetExpressionText(expression);
    }

    foreach (var item in items)
    {
        var dummy = new { Item = item };
        var guid = Guid.NewGuid().ToString();

        var memberExp = Expression.MakeMemberAccess(Expression.Constant(dummy), dummy.GetType().GetProperty("Item"));
        var singleItemExp = Expression.Lambda<Func<TModel, TValue>>(memberExp, expression.Parameters);

        sb.Append(String.Format(@"<input type=""hidden"" name=""{0}.Index"" value=""{1}"" />", htmlFieldName, guid));
        sb.Append(html.EditorFor(singleItemExp, null, String.Format("{0}[{1}]", htmlFieldName, guid)));
    }

    return new MvcHtmlString(sb.ToString());
}

虽然我不了解所有详细信息(请阅读博客文章),但我确实知道它将索引值更改为guid而不是顺序整数。这使我可以删除列表中间的项目,而无需重新计算索引。

这是我的MCVE代码的其余部分

HomeController.cs

public class HomeController : Controller
{
    [HttpGet]
    public ActionResult Index()
    {
        var model = new Race();

        //start with one already filled in
        model.HorsesInRace.Add(new Horse() { Name = "Scooby", Age = 10 });

        return View(model);
    }

    [HttpPost]
    public ActionResult Index(Race postedModel)
    {
        if (ModelState.IsValid)
            //model is valid, redirect to another page
            return RedirectToAction("ViewHorseListing");
        else
            //model is not valid, show the page again with validation errors
            return View(postedModel);
    }

    [HttpGet]
    public ActionResult AjaxMakeHorseEntry()
    {
        //new blank horse for ajax call
        var model = new List<Horse>() { new Horse() };
        return PartialView(model);
    }
}

模型.cs

public class Race
{
    public Race() { HorsesInRace = new List<Horse>(); }

    [Display(Name = "Race Name"), Required]
    public string RaceName { get; set; }

    [Display(Name = "Horses In Race")]
    public List<Horse> HorsesInRace { get; set; }
}

public class Horse
{
    [Display(Name = "Horse's Name"), Required]
    public string Name { get; set; }

    [Display(Name = "Horse's Age"), Required]
    public int Age { get; set; }
}

Index.cshtml

@model CollectionAjaxPosting.Models.Race
<h1>Race Details</h1>
@using (Html.BeginForm())
{
    @Html.ValidationSummary()
    <hr />
    <div>
        @Html.DisplayNameFor(x => x.RaceName)
        @Html.EditorFor(x => x.RaceName)
        @Html.ValidationMessageFor(x => x.RaceName)
    </div>
    <hr />
    <div id="horse-listing">@Html.EditorForMany(x => x.HorsesInRace)</div>
    <button id="btn-add-horse" type="button">Add New Horse</button>
    <input type="submit" value="Enter Horses" />
}

<script type="text/javascript">
    $(document).ready(function () {

        //add button logic
        $('#btn-add-horse').click(function () {
            $.ajax({
                url: '@Url.Action("AjaxMakeHorseEntry")',
                cache: false,
                method: 'GET',
                success: function (html) {
                    $('#horse-listing').append(html);
                }
            })
        });

        //delete-horse buttons
        $('#horse-listing').on('click', 'button.delete-horse', function () {
            var horseEntryToRemove = $(this).closest('div.horse');
            horseEntryToRemove.prev('input[type=hidden]').remove();
            horseEntryToRemove.remove();
        });

    });
</script>

视图/共享/EditorTemplates/Horse.cshtml

@model CollectionAjaxPosting.Models.Horse

<div class="horse">
    <div>
        @Html.DisplayNameFor(x => x.Name)
        @Html.EditorFor(x => x.Name)
        @Html.ValidationMessageFor(x => x.Name)
    </div>
    <div>
        @Html.DisplayNameFor(x => x.Age)
        @Html.EditorFor(x => x.Age)
        @Html.ValidationMessageFor(x => x.Age)
    </div>
    <button type="button" class="delete-horse">Remove Horse</button>
    <hr />
</div>

视图/主页/AjaxMakeHorseEntry.cshtml

@model IEnumerable<CollectionAjaxPosting.Models.Horse>

@Html.EditorForMany(x => x, "HorsesInRace")

数据流与此代码一起工作。一个人可以在页面上创建和删除任意数量的马条目,并且在提交表单时,所有输入的值都将提供给action方法。

但是,如果用户未输入有关Horse[Required]条目信息,ModelState.IsValid则将再次为false,以再次显示该表单,但是不会显示Horse属性的验证消息验证错误确实会显示在ValidationSummary列表中。

例如,如果将其Race Name与空白一起留空,Horse's Name则将显示前者的验证消息。后者将<span>使用“ field-validation-valid”类进行验证。

在此处输入图片说明

我非常确定,这是因为EditorForMany每次创建页面时方法都会为每个属性创建新的Guid,因此验证消息无法与正确的字段匹配。

我该怎么做才能解决此问题?我是否需要放弃引导索引的创建,或者可以对EditorForMany方法进行更改以允许正确传递验证消息?

马特

我非常确定,这是因为EditorForMany每次创建页面时方法都会为每个属性创建新的Guid,因此验证消息无法与正确的字段匹配。

是的; 这就是这里正在发生的事情。

要解决此问题,我们需要进行修改EditorForMany(),以使其重新使用项目的GUID,而不是生成新的项目。反过来,这意味着我们需要跟踪将哪些GUID分配给了哪个项目,以便可以重复使用它。

前者可通过对进行内部修改来实现EditorForMany()后者要求我们:

  1. 在我们的模型中添加一个属性,可以在其中存储分配的GUID
  2. 添加一个参数来EditorForMany()告诉帮助程序哪个属性包含要重用的GUID(如果有)。

这使EditorForMany助手看起来像这样;

public static class HtmlHelperExtensions
{
    public static MvcHtmlString EditorForMany<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, IEnumerable<TValue>>> propertyExpression, Expression<Func<TValue, string>> indexResolverExpression = null, string htmlFieldName = null) where TModel : class
    {
        htmlFieldName = htmlFieldName ?? ExpressionHelper.GetExpressionText(propertyExpression);

        var items = propertyExpression.Compile()(html.ViewData.Model);
        var htmlBuilder = new StringBuilder();
        var htmlFieldNameWithPrefix = html.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName);
        Func<TValue, string> indexResolver = null;

        if (indexResolverExpression == null)
        {
            indexResolver = x => null;
        }
        else
        {
            indexResolver = indexResolverExpression.Compile();
        }

        foreach (var item in items)
        {
            var dummy = new { Item = item };
            var guid = indexResolver(item);
            var memberExp = Expression.MakeMemberAccess(Expression.Constant(dummy), dummy.GetType().GetProperty("Item"));
            var singleItemExp = Expression.Lambda<Func<TModel, TValue>>(memberExp, propertyExpression.Parameters);

            if (String.IsNullOrEmpty(guid))
            {
                guid = Guid.NewGuid().ToString();
            }
            else
            {
                guid = html.AttributeEncode(guid);
            }

            htmlBuilder.Append(String.Format(@"<input type=""hidden"" name=""{0}.Index"" value=""{1}"" />", htmlFieldNameWithPrefix, guid));

            if (indexResolverExpression != null)
            {
                htmlBuilder.Append(String.Format(@"<input type=""hidden"" name=""{0}[{1}].{2}"" value=""{1}"" />", htmlFieldNameWithPrefix, guid, ExpressionHelper.GetExpressionText(indexResolverExpression)));
            }

            htmlBuilder.Append(html.EditorFor(singleItemExp, null, String.Format("{0}[{1}]", htmlFieldName, guid)));
        }

        return new MvcHtmlString(htmlBuilder.ToString());
    }
}

然后,我们还需要更改模型,以添加用于存储GUID的属性。

public class Race
{
    public Race() { HorsesInRace = new List<Horse>(); }

    [Display(Name = "Race Name"), Required]
    public string RaceName { get; set; }

    [Display(Name = "Horses In Race")]
    public List<Horse> HorsesInRace { get; set; }
}

public class Horse
{
    [Display(Name = "Horse's Name"), Required]
    public string Name { get; set; }

    [Display(Name = "Horse's Age"), Required]
    public int Age { get; set; }

    // Note the addition of Index here.
    public string Index { get; set; }
}    

...最后,更改我们的用法EditorForMany()以使用新签名;

Index.cshtml ;

<div id="horse-listing">@Html.EditorForMany(x => x.HorsesInRace, x => x.Index)</div>

AjaxMakeHorseEntry.cshtml ;

@Html.EditorForMany(x => x, x => x.Index, "HorsesInRace")

...,然后应显示验证消息。


顺便说一句,我建议不要将htmlFieldName参数用于EditorForMany,而应将控制器操作更改为;

[HttpGet]
public ActionResult AjaxMakeHorseEntry()
{
    var model = new Race();

    model.HorsesInRace.Add(new Horse());
    return PartialView(model);
}

...那么您的AjaxMakeHorseEntry.cshtml视图就是公正的;

@model Models.Race

@Html.EditorForMany(x => x.HorsesInRace, x => x.Index)

否则,name当嵌套使用时,生成的属性会中断EditorForMany()

由于这个原因,我将更新博客文章以使用的上述版本EditorForMany(),而不是接受htmlFieldName参数。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

在属性上使用sum验证集合

来自分类Dev

如何在验证消息Laravel 5.2中获取数组索引

来自分类Dev

当状态/属性更改时,Antd 3.26表单验证消息不会重新呈现新值

来自分类Dev

每次发送或接收消息时,如何使最后联系的用户出现在recyclerview列表的顶部?

来自分类Dev

单击该元素时如何使用HTML获取HTML集合中某个元素的索引

来自分类Dev

C#-如何使用XmlSchemaSet验证XML时在错误消息中获取正确的行号?

来自分类Dev

每次状态更新时如何呈现元素

来自分类Dev

验证失败时如何使用先前输入的数据呈现表单

来自分类Dev

使用Serilog时如何在输出消息中获取丰富的属性

来自分类Dev

在iOS上使用Crypto ++验证消息时出错

来自分类Dev

当消息来自属性时,不使用新行的alert()

来自分类Dev

调整大小的导航控制器以模态形式呈现在UIModalPresentationFormSheet上,当每个新的视图控制器被推送时

来自分类Dev

如何使用wsadmin在WAS 7上获取身份验证别名的属性

来自分类Dev

如何配置消息属性以使用注释弹簧进行验证

来自分类Dev

每次命中索引时如何执行代码?

来自分类Dev

使用验证批注时,如何强制显示错误消息的顺序?

来自分类Dev

使用Blazor验证时如何清除错误消息

来自分类Dev

MongoDB聚合:如何获取集合中文档的索引,具体取决于按文档属性排序

来自分类Dev

在错误验证消息上获取组件ID

来自分类Dev

如何获取属性而不是集合属性

来自分类Dev

如何使用新对象填充集合,从另一个集合复制属性

来自分类Dev

在列表上使用izip时获取特定索引

来自分类Dev

React useEffect,如何在每次值更新时重新呈现

来自分类Dev

如何在 NodeJs 中的 API 获取请求成功时呈现到新的 HTML 页面?

来自分类Dev

每次我在服务器上打开新终端时如何执行命令

来自分类Dev

在使用Vim编辑时,如何使提交差异出现在提交消息中?

来自分类Dev

使用虚拟属性时如何添加错误消息

来自分类Dev

Laravel 5.4 集合 - 如何通过索引从集合中获取数据

来自分类Dev

如何在每次使用Selenium时从instagram获取instagram的发布网址,每次向下滚动时它都会动态变化?

Related 相关文章

  1. 1

    在属性上使用sum验证集合

  2. 2

    如何在验证消息Laravel 5.2中获取数组索引

  3. 3

    当状态/属性更改时,Antd 3.26表单验证消息不会重新呈现新值

  4. 4

    每次发送或接收消息时,如何使最后联系的用户出现在recyclerview列表的顶部?

  5. 5

    单击该元素时如何使用HTML获取HTML集合中某个元素的索引

  6. 6

    C#-如何使用XmlSchemaSet验证XML时在错误消息中获取正确的行号?

  7. 7

    每次状态更新时如何呈现元素

  8. 8

    验证失败时如何使用先前输入的数据呈现表单

  9. 9

    使用Serilog时如何在输出消息中获取丰富的属性

  10. 10

    在iOS上使用Crypto ++验证消息时出错

  11. 11

    当消息来自属性时,不使用新行的alert()

  12. 12

    调整大小的导航控制器以模态形式呈现在UIModalPresentationFormSheet上,当每个新的视图控制器被推送时

  13. 13

    如何使用wsadmin在WAS 7上获取身份验证别名的属性

  14. 14

    如何配置消息属性以使用注释弹簧进行验证

  15. 15

    每次命中索引时如何执行代码?

  16. 16

    使用验证批注时,如何强制显示错误消息的顺序?

  17. 17

    使用Blazor验证时如何清除错误消息

  18. 18

    MongoDB聚合:如何获取集合中文档的索引,具体取决于按文档属性排序

  19. 19

    在错误验证消息上获取组件ID

  20. 20

    如何获取属性而不是集合属性

  21. 21

    如何使用新对象填充集合,从另一个集合复制属性

  22. 22

    在列表上使用izip时获取特定索引

  23. 23

    React useEffect,如何在每次值更新时重新呈现

  24. 24

    如何在 NodeJs 中的 API 获取请求成功时呈现到新的 HTML 页面?

  25. 25

    每次我在服务器上打开新终端时如何执行命令

  26. 26

    在使用Vim编辑时,如何使提交差异出现在提交消息中?

  27. 27

    使用虚拟属性时如何添加错误消息

  28. 28

    Laravel 5.4 集合 - 如何通过索引从集合中获取数据

  29. 29

    如何在每次使用Selenium时从instagram获取instagram的发布网址,每次向下滚动时它都会动态变化?

热门标签

归档