加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 每次使用新的guid索引时,如何在集合属性上获取验证消息?

发布时间:2020-12-15 08:48:14 所属栏目:百科 来源:网络整理
导读:在这个示例ASP.Net MVC 4程序中,我有一个用户填写有关赛马的详细信息.比赛有一个名字,以及所涉及的马匹列表.每匹马都有一个名字和一个年龄. 表单使用ajax和javascript来允许此人动态添加和删除马输入字段,然后在按下提交按钮时立即提交. 为了让这个过程变得
在这个示例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}]",guid)));
    }

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

虽然我不了解所有细节(请阅读博客文章),但我知道它将索引值更改为guids而不是顺序整数.这允许我删除列表中间的项目,而无需重新计算索引.

这是我的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);
    }
}

Models.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")

数据流与此代码一起使用.一个人能够在页面上尽可能多地创建和删除马条目,并且在提交表单时,所有输入的值都被赋予操作方法.

但是,如果用户未输入马条目的[必需]信息,则ModelState.IsValid将显示为false,同时显示该表单,但不会显示Horse属性的验证消息.验证错误确实显示在ValidationSummary列表中.

例如,如果将“种族名称”留空,并且还有一个“马”的名称,则会显示前者的验证消息.后者将进行验证< span>使用“field-validation-valid”类.

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

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

解决方法

I’m very sure this is caused because the EditorForMany method creates new guids for each property each time the page is created,so validation messages can’t be matched to the correct field.

是的;这正是这里发生的事情.

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

前者可以通过对EditorForMany()的内部修改来完成.后者要求我们:

>将属性添加到我们的模型中,可以在其中存储指定的GUID
>向EditorForMany()添加一个参数,告诉助手哪个属性包含要重用的GUID(如果有).

这使得EditorForMany帮助器看起来像这样;

public static class HtmlHelperExtensions
{
    public static MvcHtmlString EditorForMany<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,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}"" />",guid,ExpressionHelper.GetExpressionText(indexResolverExpression)));
            }

            htmlBuilder.Append(html.EditorFor(singleItemExp,guid)));
        }

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

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

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

    [Display(Name = "Race Name"),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")

…然后应该出现验证消息.

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

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

否则,嵌套使用EditorForMany()时生成的名称属性会中断.

我将更新博客文章以使用上述版本的EditorForMany(),而不是接受htmlFieldName参数,因此.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读