【问题标题】:Two Ajax forms, one view model. Validation only works for one两个 Ajax 表单,一个视图模型。验证仅适用于一个
【发布时间】:2015-11-24 03:21:31
【问题描述】:

我的页面上有两个表格。我的视图模型如下所示:

public class HomeViewModel
{
    [Required(ErrorMessage = "We kind of need an email address!")]
    [EmailAddress(ErrorMessage = "This isn't an email address!")]
    public string Email { get; set; }
    public ContactForm ContactForm { get; set; }  
}

联系方式:

public class ContactForm
{
    [Required(ErrorMessage = "We need your name, please!")]
    public string Name { get; set; }
    [Required(ErrorMessage = "We need your email, please!")]
    [EmailAddress(ErrorMessage = "This isn't an email address!")]
    public string EmailAddress { get; set; }
    [Required(ErrorMessage = "Please elaborate a little!")]
    public string Message { get; set; }
}

第一个表单动作:

public ActionResult FreeConsultSubmit(HomeViewModel model)
    {
        if (ModelState.IsValid)
        {
            //do stuff
        }
        return PartialView("_SubmitResult", false);

    }

第二次行动:

public ActionResult ContactSubmit(HomeViewModel model)
    {
        if (ModelState.IsValid)
        {
            //dostuff
        }
        return PartialView("_SubmitContactResult", false);
    }

第一个 Ajax 表单

@using (Ajax.BeginForm("FreeConsultSubmit", new AjaxOptions()
    {
        InsertionMode = InsertionMode.Replace,
        UpdateTargetId = "FreeConsultResults"
    }))
    {
        <div id="FreeConsultResults">
            <div class="row">
                <div class="small-4 columns small-centered text-center splashEmail">
                    @Html.TextBoxFor(m => m.Email, new { @placeholder = "Please enter your email..." })
                    @Html.ValidationMessageFor(m => m.Email)
                </div>
            </div>
            <div class="row">
                <div class="small-5 columns small-centered text-center">
                    <input type="submit" class="hvr-border-fade splashCallToAction" value="Get Started" />
                </div>
            </div>
        </div>
    }

第二种 Ajax 形式:

@using (Ajax.BeginForm("ContactSubmit", new AjaxOptions()
        {
            InsertionMode = InsertionMode.Replace,
            UpdateTargetId = "ContactSubmitResults"
        }))
    {
        <div id="ContactSubmitResults">
            <div class="row">
                <div class="small-12 columns small-centered">
                    @Html.TextBoxFor(m => m.ContactForm.Name, new { @placeholder = "Your Name" })
                    @Html.ValidationMessageFor(m => m.ContactForm.Name)
                    @Html.TextBoxFor(m => m.ContactForm.EmailAddress, new { @placeholder = "Your Email Address" })
                    @Html.ValidationMessageFor(m => m.ContactForm.EmailAddress)
                    @Html.TextAreaFor(m => m.ContactForm.Message, new { @placeholder = "Your Message", @class = "contactMessage" })
                    @Html.ValidationMessageFor(m => m.ContactForm.Message)
                </div>
            </div>
            <div class="row">
                <div class="small-12 columns small-centered text-center">
                    <a href="">
                        <input type="submit" class="hvr-border-fade sendMessageSmall" value="Send Message" />
                    </a>
                </div>
            </div>
        </div>
    }

我已经把所有东西都连接好了,客户端验证也能正常工作。这可能有点矫枉过正,但我​​也想设置服务器端验证。

问题是,在提交第一个表单(只是电子邮件字符串)时,我检查 ModelState 是否有效,并且确实有效。如果您深入了解 ModelState,您会看到只查看了 1 个属性。

如果您提交第二个表单(ContactForm),ModelState.IsValid 返回 false,如果您向下钻取,您会看到它正在查看 4 个属性(3 个 ContactForm 属性,加上字符串电子邮件)。由于需要电子邮件字符串,因此失败。

我很困惑为什么它对一个有效,而对另一个无效。我可以删除服务器端验证,但我至少想知道为什么会这样。我也可以从 ModelState 中删除错误,但这似乎一点也不优雅。

【问题讨论】:

  • 你还没有展示你的ContactForm模型(它有验证属性吗?)或者你的视图或控制器!
  • 完成。非常直截了当,所有的绑定都有效。只是第二个表单尝试验证第一个表单中的第一个字符串电子邮件字段。
  • 您的模型以这种方式设置是否有特殊原因?似乎您或多或少地复制了一个属性(电子邮件),即使您不使用它。
  • 第二种形式尝试验证Email 属性,因为它是您模型的一部分,并且它具有[Required] 属性,并且您没有为它发布值。一个非常奇怪的设计,但解决它的一种方法是将方法更改为public ActionResult ContactSubmit([Bind(Prefix = "ContactForm")]ContactForm model)
  • 我有一个号召性用语,只需要页面顶部的电子邮件和底部的联系表。所以我不是在复制一个属性,而是在使用它。为什么这是一个奇怪的设计?谢谢斯蒂芬,这似乎是最好的解决方案。

标签: asp.net-mvc


【解决方案1】:

如果您只是想在一个视图中包含两个单独的表单,您最好将表单拆分为单独的“子视图”和子操作,然后使用@Html.Action() 将它们呈现到位。

这是一个例子:

型号

我会从 HomeViewModel 中删除 ContactForm 模型,并将 HomeViewModel 重命名为 ConsultingForm(以匹配联系人模型的命名约定):

public class ConsultingForm
{
    [Required(ErrorMessage = "We kind of need an email address!")]
    [EmailAddress(ErrorMessage = "This isn't an email address!")]
    public string Email { get; set; }
}

public class ContactForm
{
    [Required(ErrorMessage = "We need your name, please!")]
    public string Name { get; set; }
    [Required(ErrorMessage = "We need your email, please!")]
    [EmailAddress(ErrorMessage = "This isn't an email address!")]
    public string EmailAddress { get; set; }
    [Required(ErrorMessage = "Please elaborate a little!")]
    public string Message { get; set; }
}

控制器

添加“子”操作,如下所示:

public class HomeController : Controller
{
    public ActionResult Home()
    {
        return View();
    }

    [ChildActionOnly, HttpGet]
    public ActionResult ConsultingRequest()
    {
        var model = new ConsultingForm();

       return View(model);
    }

    [ChildActionOnly, HttpGet]
    public ActionResult ContactRequest()
    {
        var model = new ContactForm();

       return View(model);
    }
}

ChildActionOnlyAttribute 将该动作标记为子动作。来自MSDN

子操作方法为视图的一部分呈现内联 HTML 标记 而不是渲染整个视图。

观看次数

您的第一个子视图将与您已有的相同:

@using (Ajax.BeginForm("FreeConsultSubmit", new AjaxOptions()
    {
        InsertionMode = InsertionMode.Replace,
        UpdateTargetId = "FreeConsultResults"
    }))
    {
        <div id="FreeConsultResults">
            <div class="row">
                <div class="small-4 columns small-centered text-center splashEmail">
                    @Html.TextBoxFor(m => m.Email, new { @placeholder = "Please enter your email..." })
                    @Html.ValidationMessageFor(m => m.Email)
                </div>
            </div>
            <div class="row">
                <div class="small-5 columns small-centered text-center">
                    <input type="submit" class="hvr-border-fade splashCallToAction" value="Get Started" />
                </div>
            </div>
        </div>
    }

您的第二个子视图只需要删除属性绑定中的额外“步骤”:

@using (Ajax.BeginForm("ContactSubmit", new AjaxOptions()
    {
        InsertionMode = InsertionMode.Replace,
        UpdateTargetId = "ContactSubmitResults"
    }))
{
    <div id="ContactSubmitResults">
        <div class="row">
            <div class="small-12 columns small-centered">
                @Html.TextBoxFor(m => m.Name, new { @placeholder = "Your Name" })
                @Html.ValidationMessageFor(m => m.Name)
                @Html.TextBoxFor(m => m.EmailAddress, new { @placeholder = "Your Email Address" })
                @Html.ValidationMessageFor(m => m.EmailAddress)
                @Html.TextAreaFor(m => m.Message, new { @placeholder = "Your Message", @class = "contactMessage" })
                @Html.ValidationMessageFor(m => m.Message)
            </div>
        </div>
        <div class="row">
            <div class="small-12 columns small-centered text-center">
                <a href="">
                    <input type="submit" class="hvr-border-fade sendMessageSmall" value="Send Message" />
                </a>
            </div>
        </div>
    </div>
}

“包装器”或父视图看起来像:

<div class="whatever">

    @Html.Action("ConsultingRequest", "Home")

    @Html.Action("ContactRequest", "Home")

</div>

如果您检查呈现的 HTML,您会发现每个表单都正确绑定到其模型的属性,因此当您发布每个表单时,只有这些属性是模型绑定和验证的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-15
    • 2013-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多