当前位置 : 主页 > 网络编程 > ASP >

asp.net-mvc – ASP.NET MVC中具有动态数量的texbox的表单

来源:互联网 收集:自由互联 发布时间:2021-06-24
考虑以下模型和控制器: public class SimpleModel{ [Required(ErrorMessage="Email Address is required.")] [DataType(DataType.EmailAddress)] [DisplayName("EmailAddress")] public string EmailAddress { get; set; }}[HandleError]public c
考虑以下模型和控制器:

public class SimpleModel
{
    [Required(ErrorMessage="Email Address is required.")]
    [DataType(DataType.EmailAddress)]
    [DisplayName("EmailAddress")]
    public string EmailAddress { get; set; }
}

[HandleError]
public class SimpleController : Controller
{
    public ActionResult Simple()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Simple(SimpleModel model)
    {
        if (ModelState.IsValid)
        {
            // handling code here
        }

        return View(model);
    }
}

…以及匹配视图中的相关部分:

<% using (Html.BeginForm()) { %>
    <%= Html.ValidationSummary(true, "The form submitted is not valid.") %>
    <div> 
        <fieldset>
            <div class="editor-label">
                <%= Html.LabelFor(m => m.EmailAddress)%>
            </div>
            <div class="editor-field">
                <%= Html.TextBoxFor(m => m.EmailAddress)%>
                <%= Html.ValidationMessageFor(m => m.EmailAddress)%>
            </div>

            <div class="editor-field">
                <input type="submit" value="Submit" />
            </div>
        </fieldset>
    </div>
<% } %>

修改模型,视图和控制器以支持控制器定义的动态数量的电子邮件地址的最佳方法是什么.

根据 Steve Sanderson的文章,我能够找到我想要的优雅解决方案:

首先,模型需要修改如下:

public class SimpleModel
{
    public IEnumerable<EmailAddress> EmailAddresses { get; set; }
}

public class EmailAddress
{
    [Required(ErrorMessage = "Email Address is required.")]
    [DataType(DataType.EmailAddress)]
    [DisplayName("Email Address")]
    public string Value { get; set; }        
}

处理GET方法的控制器方法需要根据需要使用尽可能多的条目预填充模型:

[HandleError]
public class SimpleController : Controller
{
    public ActionResult Simple()
    {
        SimpleModel model = new SimpleModel
            {
                EmailAddresses =
                    new List<EmailAddress>
                        { 
                            // as many as required
                            new EmailAddress { Value = string.Empty }, 
                            new EmailAddress { Value = string.Empty },
                            new EmailAddress { Value = string.Empty }
                        }
            };
        return View(model);
    }

    [HttpPost]
    public ActionResult Simple(SimpleModel model)
    {
        if (ModelState.IsValid)
        {
            // handling code here
        }

        return View(model);
    }
}

视图还需要改变:

<% using (Html.BeginForm()) { %>
    <%= Html.ValidationSummary(true, "The form submitted is not valid.") %>
    <div> 
        <fieldset>
            <% foreach (var item in Model.EmailAddresses)
                   Html.RenderPartial("SimpleRows", item);
             %>

            <div class="editor-field">
                <input type="submit" value="Submit" />
            </div>

        </fieldset>
    </div>
<% } %>

……需要创建一个新的局部视图.请注意,视图是强类型的,具有集合项的类型.

<% using(Html.BeginCollectionItem("EmailAddresses")) { %>
<div class="editor-label">
    <%= Html.LabelFor(x => x.Value)%>
</div>
<div class="editor-field">
    <%= Html.TextBoxFor(x => x.Value)%>
    <%= Html.ValidationMessageFor(x => x.Value)%>
</div>
<% }%>

BeginCollectionItem是Sanderson创建的Helper方法:

public static class HtmlPrefixScopeExtensions
{
    private const string idsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_";

    public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName)
    {
        var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName);
        string itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString();

        // autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync.
        html.ViewContext.Writer.WriteLine(string.Format("<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", collectionName, html.Encode(itemIndex)));

        return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex));
    }

    public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix)
    {
        return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
    }

    private static Queue<string> GetIdsToReuse(HttpContextBase httpContext, string collectionName)
    {
        // We need to use the same sequence of IDs following a server-side validation failure,  
        // otherwise the framework won't render the validation error messages next to each item.
        string key = idsToReuseKey + collectionName;
        var queue = (Queue<string>)httpContext.Items[key];
        if (queue == null) {
            httpContext.Items[key] = queue = new Queue<string>();
            var previouslyUsedIds = httpContext.Request[collectionName + ".index"];
            if (!string.IsNullOrEmpty(previouslyUsedIds))
                foreach (string previouslyUsedId in previouslyUsedIds.Split(','))
                    queue.Enqueue(previouslyUsedId);
        }
        return queue;
    }

    private class HtmlFieldPrefixScope : IDisposable
    {
        private readonly TemplateInfo templateInfo;
        private readonly string previousHtmlFieldPrefix;

        public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix)
        {
            this.templateInfo = templateInfo;

            previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
            templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
        }

        public void Dispose()
        {
            templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix;
        }
    }
}

…就是这样……当您发布表单时,模型将自动填充并传递给处理POST方法的控制器操作.

请注意,使用此解决方案,所有属性(包括验证)都按预期工作.

网友评论