如何在具有相同模型的同一 MVC 页面上重用表单?

Posted

技术标签:

【中文标题】如何在具有相同模型的同一 MVC 页面上重用表单?【英文标题】:How to reuse form on the same MVC page with the same model? 【发布时间】:2018-04-02 04:58:44 【问题描述】:

我有一个包含姓名、电子邮件和电话的简单联系表单的视图。我在同一个父页面上重复使用此表单两到三次。当用户提交其中一个表单时,我检测到提交的表单并使用 Dapper 通过 javascript/jQuery 将内容发布到数据库。

问题是当我在任何页面上重复使用联系表单时,输入字段将不会生成唯一 ID。这会导致 w3 验证失败,提示 Duplicate ID。在这种特殊情况下,我需要页面通过 w3 验证。

我将如何解决这个问题?我尝试使用ViewData.TemplateInfo.htmlFieldPrefix 为输入字段添加前缀,但并没有真正解决问题,因为前缀值是静态的。如果我做一个随机前缀值,那么如何在 HttpPost 控制器中捕获它?

这是我的代码。

Index.cshtml - 多次引用ContactForm.cshtml

<html>
<head>...</head>
<body>
....
 @Html.RenderAction("ContactForm")
....
 @Html.RenderAction("ContactForm")
....
</body>
</html>

ContactForm.cshtml

@model ProjectX.Models.CaseModel
@using (Html.BeginForm("SendMessage", "Home", FormMethod.Post))

    @Html.TextInputFor(model => model.Name, Res.Name, new  placeholder = Res.Name  )
    @Html.TextInputFor(model => model.Phone, Res.Phone, new  placeholder = Res.Phone, type = "tel" )
    @Html.TextInputFor(model => model.Email, Res.Email, new  placeholder = Res.Email, type = "email" )
    <input type="submit" value="Send" onclick="jsSaveForm(event);" />


// @Html.TextInputFor MvcHtmlString helper method 
public static MvcHtmlString TextInputFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string title, Object htmlAttributes = null)

    var req = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).IsRequired;
    var name = helper.ViewData.TemplateInfo.GetFullHtmlFieldId(ExpressionHelper.GetExpressionText(expression));
    if (req)
        title += " *";

    string html = String.Format("<div class=\"inp\"><label for=\"2\">0</label>1</div>", helper.ValidationMessageFor(expression), helper.TextBoxFor(expression, htmlAttributes), name);
    return new MvcHtmlString(html);

CaseModel.cs

public class CaseModel

    [Required(ErrorMessageResourceType = typeof(Res), ErrorMessageResourceName = "ValidationRequired")]
    public string Name  get; set; 
    public string Phone  get; set; 
    public string Email  get; set; 

HomeController

// GET
[ChildActionOnly]
[Route("~/Home/ContactForm")]
public ActionResult ContactForm()

    // tried this but this is a static value. 
    // ViewData.TemplateInfo.HtmlFieldPrefix = "SomePrefix";

    return PartialView(new CaseModel());


// POST
[HttpPost]
[Route("~/Home/SendMessage")]
public async Task<PartialViewResult> SendMessage(CaseModel model)

   ... brevity...
   await SaveData(model);

【问题讨论】:

为什么需要前缀?您要在同一页面中多次插入表单吗? 您可以随时删除 id 属性 - new id = "", placeholder = Res.Name @Shyju - 也许我不需要前缀,但如果相同的 ID 在页面上出现两次,w3 验证将失败,在这种情况下不能。另外,我做了一些 jquery 客户端验证——例如该字段是否为空等。 @StephenMuecke - 不会删除 id 这会导致客户端验证失败? id 属性与客户端验证无关 【参考方案1】:

例如,您可以通过将id 属性设置为htmlAttributes 参数中的空字符串来删除它

@Html.TextInputFor(model => model.Name, Res.Name, new  id = "", placeholder = Res.Name  )

尽管您可能想考虑在您的 TextInputFor() 扩展方法中执行此操作。

附带说明,您的扩展方法不考虑属性的显示名称(使用[Display(Name = "...")] 属性时,title 参数是不必要的。我建议您的代码应该是

public static MvcHtmlString TextInputFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes = null)

    ModelMetadata metaData = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
    string displayName = metaData.DisplayName;
    if (metaData.IsRequired)
    
        displayName += " *";
    
    StringBuilder html = new StringBuilder();
    html.Append(helper.LabelFor(expression, displayName).ToString());
    html.Append(helper.TextBoxFor(expression, htmlAttributes).ToString());
    html.Append(helper.ValidationMessageFor(expression).ToString());
    TagBuilder container = new TagBuilder("div");
    container.AddCssClass("inp");
    container.InnerHtml = html.ToString();
    return new MvcHtmlString(container.ToString());

【讨论】:

非常感谢! :) 出于好奇,如果一个人真的需要 id's 是独一无二的 - 如何实现? 拥有 id 属性的唯一原因是用于 css 或 javascript/jQuery 选择器,因此您可能不需要它们,但有一些选项,例如,创建和将Guid 附加到助手生成的id 上,(但这会使它们在css 或javascript 选择器方面毫无用处) 附带说明,关于标签的扩展方法也存在缺陷 - 它不会使用您可能应用于属性的任何 [Display(Name = "...")] 属性的值。而且您方法中的第二个参数似乎毫无意义。当我有机会时,我会用你扩展方法的外观来更新答案,包括如何删除帮助程序本身中的 id 属性 我对进一步开发的想法是,我会在 jQuery 中引用其中的一些,或者只是在 vanilla js 中引用它们。执行Guid 听起来是个好主意,但您是否不必像[Bind(Prefix=Guid here?)] CaseModel model 那样以某种方式告诉HttpPost 控制器有关此动态Guid 的信息? 我的属性是这样的:[Required(ErrorMessageResourceType = typeof(Res), ErrorMessageResourceName = "ValidationRequired")] public string Name get; set; - 这似乎适用于扩展。

以上是关于如何在具有相同模型的同一 MVC 页面上重用表单?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Laravel 测试两个具有相同输入名称的表单

Django - 在多个页面上重用用户注册表单的系统架构

在rails中重用相同的脚本

如何通过在同一位置映射两个变量来重用物理内存?

ASP MVC 5 从链接发布表单

.NET MVC Ajax 表单保留旧的输入值 - 如何更新到模型的值?