Asp.Net MVC - 如果“保存”如何重新加载表单数据数据在包含多个表单的视图中失败

发布于 2024-10-06 17:03:44 字数 8892 浏览 0 评论 0原文

我有一个页面,允许用户编辑联系人的详细信息。它显示基本详细信息(姓名、地址等)、已知电子邮件地址列表和电话号码列表,并允许他们添加/编辑电子邮件和电话详细信息。我的视图具有三种形式,一种用于基本详细信息,一种用于电子邮件详细信息,一种用于电话详细信息 - 每一种都发布到不同的控制器操作(SaveDetails、SaveEmail、SaveTelephone)。问题是,如果用户要添加新电子邮件并且 ModelState 无效,那么一旦我返回到主要详细信息 (HttpGet) 控制器方法以显示页面和模型状态错误,那么我就会丢失用户输入的数据进入电子邮件表格。

我真的不想有一个包含所有详细信息的大表单,并且我想避免使用 TempData 将表单输入存储/传递回主页 - 有人对如何实现这一目标有任何建议吗?

这是我的视图模型 ContactDetailsViewModel,其中包含 EmailDetailsViewModel 和 TelephoneFaxDetailsViewModel:

public class ContactDetailsViewModel : MasterViewModel
{
    public int ContactId { get; set; }
    public int? EmailId { get; set; }
    public int? TelephoneFaxId { get; set; }

    [DisplayName("First Name")]
    [StringLength(30, ErrorMessage = "First Name cannot be more than 30 characters")]
    public string FirstName { get; set; }

    [DisplayName("Last Name")]
    [StringLength(30, ErrorMessage = "Last Name cannot be more than 30 characters")]
    public string LastName { get; set; }

    public IList<EmailSummary> EmailSummaries { get; set; }
    public IList<TelephoneFaxSummary> TelephoneFaxSummaries { get; set; }

    public EmailDetailsViewModel EmailDetails { get; set; }
    public TelephoneFaxDetailsViewModel TelFaxDetails { get; set; }
}

以及我的视图,其中包含 3 个单独的表单,并且强类型化为 ContactDetailsViewModel:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<SPP.Portal.ViewModels.Contacts.ContactDetailsViewModel>" %>
<% using (Html.BeginForm("Details","Contact", FormMethod.Post))
   {%>
        <%: Html.ValidationSummary() %>
<fieldset>
    <%: Html.HiddenFor(m => m.EmailId) %>
    <%: Html.HiddenFor(m => m.TelephoneFaxId) %>

    <div class="editor-field">
        <div  class="left">
            <%: Html.LabelFor(model => model.FirstName) %>
            <%: Html.TextBoxFor(model => model.FirstName)%>
            <%: Html.ValidationMessageFor(model => model.FirstName)%>
        </div>
        <div class="right">
            <%: Html.LabelFor(model => model.LastName) %>
            <%: Html.TextBoxFor(model => model.LastName)%>
            <%: Html.ValidationMessageFor(model => model.LastName)%>
        </div>
    </div>

    <p>
        <input type="button" onclick="addEmail();" id="email" value="Add New Email" />
        <input type="button" onclick="addTelephoneFax();" id="telFax" value="Add New Telephone/Fax" />
        <input name="button" id="Save"type="submit" value="Save" />
        <input name="button" id="Return" type="submit" value="Return" />
    </p>

    <%
        } %>
    <fieldset>
        <legend>All Email Details</legend>
        <table>
            <tr>
                <th>Edit</th>
                <th>Email Address</th>
                <th>Email Type</th>
                <th>Delete</th>
            </tr>
            <% foreach (var emailSummary in Model.EmailSummaries)
               { %>
            <tr>
                <%: Html.Hidden(emailSummary.EmailId.ToString()) %>
                <td>
                   <a href="<%: ResolveUrl(emailSummary.EditUrl.OriginalString) %>">Edit</a>
                </td>
                <td>
                    <%: emailSummary.EmailAddress %>
                </td>
                <td>
                    <%: emailSummary.EmailType %>
                </td>
                <td>
                 <a href="<%: ResolveUrl(emailSummary.DeleteUrl.OriginalString) %>" class="confirmDelete">Delete</a>
                </td>
            </tr>
            <% } %>
        </table>
    </fieldset>

    <% using (Html.BeginForm("EmailDetails", "Contact", FormMethod.Post))
     {%>
    <div id="EmailDetails">
    <fieldset>
        <legend>Email</legend>

        <%: Html.HiddenFor(m => m.EmailDetails.Id)%>
        <%: Html.HiddenFor(m => m.EmailDetails.ContactId)%>
        <div class="editor-field">
            <div class="right">
                <%: Html.LabelFor(m => m.EmailDetails.EmailAddress)%>
                <%: Html.TextBoxFor(m => m.EmailDetails.EmailAddress)%>
                <%: Html.ValidationMessageFor(model => model.EmailDetails.EmailAddress)%>
            </div>
        </div>
        <div class="editor-field">
            <div class="left">
                <%: Html.LabelFor(m => m.EmailDetails.EmailType)%>
                <%: Html.DropDownListFor(m => m.EmailDetails.EmailType, Model.EmailDetails.EmailTypeCodes)%>
                <%: Html.ValidationMessageFor(model => model.EmailDetails.EmailType)%>
            </div>
        <p>
            <input type="submit" id="EmailSave" value="Save" />
            <input type="submit" name="button" id="EmailCancel" value="Cancel" />
        </p>
        </fieldset> 
    <% } %> 
    </div>

    <fieldset>
        <legend>All Telephone/Fax Details</legend>
        <table>
            <tr>
                <th>Edit</th>
                <th>Dialing Code</th>
                <th>Telephone/Fax Number</th>
                <th>Delete</th>
            </tr>
            <% foreach (var telFaxSummary in Model.TelephoneFaxSummaries)
               { %>
            <tr>
                <%: Html.Hidden(telFaxSummary.TelephoneFaxId.ToString())%>
                <td>
                   <a href="<%:ResolveUrl(telFaxSummary.EditUrl.OriginalString)%>" >Edit</a>
                </td>
                <td>
                    <%: telFaxSummary.DialingCode%>
                </td>
                <td>
                    <%: telFaxSummary.TelephoneFaxNumber%>
                </td>
                <td>
                  <a href="<%:ResolveUrl(telFaxSummary.DeleteUrl.OriginalString)%>" class="confirmDelete">Delete</a>
                </td>
            </tr>
            <% } %>
        </table>
    </fieldset>

    <% using (Html.BeginForm("TelephoneFaxDetails", "Contact", FormMethod.Post))
       {%>
        <div id="TelephoneFaxDetails">
        <%: Html.HiddenFor(m => m.TelFaxDetails.TelephoneFaxId)%>
        <%: Html.HiddenFor(m => m.TelFaxDetails.ContactId)%>
        <fieldset>
                <legend>Telephone/Fax</legend>
        <div class="editor-field">
            <div id="left" class="left">
                <%: Html.LabelFor(m => m.TelFaxDetails.InternationalDialingCode)%>
                <%: Html.TextBoxFor(m => m.TelFaxDetails.InternationalDialingCode)%>
                <%: Html.ValidationMessageFor(model => model.TelFaxDetails.InternationalDialingCode)%>
            </div>
            <div id="middle" class="right">
                <%: Html.LabelFor(m => m.TelFaxDetails.TelephoneFaxNumber)%>
                <%: Html.TextBoxFor(m => m.TelFaxDetails.TelephoneFaxNumber)%>
                <%: Html.ValidationMessageFor(model => model.TelFaxDetails.TelephoneFaxNumber)%>
            </div>
        </div>
            <p>
                <input type="submit" id="TelephoneSave" value="Save" />
                <input type="submit" name="button" id="TelephoneCancel" value="Cancel" />
            </p>
        </fieldset> 
    <% } %>
    </div>

然后我的控制器方法添加新电子邮件:

    [HttpPost]
    public ActionResult EmailDetails(ContactDetailsViewModel contactViewModel)
    {
        var viewModel = contactViewModel.EmailDetails;

        if (ModelState.IsValid)
        {
            try
            {
                _viewModelService.UpdateEmail(User, viewModel);

                return RedirectToAction("Details");
            }
            catch (Exception ex)
            {
                ModelState.AddModelError("Exception", "The Email was not updated.");

                return RedirectToAction("Details");
            }
        }

        return View("Details", contactViewModel);
    }

所以想法是,如果我将整个 ContactViewModel 放入电子邮件帖子中,那么如果模型无效,那么我应该能够返回包含基本联系人的整个详细信息 ContactViewModel用户刚刚输入的详细信息和电子邮件详细信息,这样他们就不会丢失数据。电子邮件详细信息可以很好地传递到控制器,但是当传递到控制器时,所有基本联系数据均为空 - 有什么想法吗?

I have a page which allows a user to edit the details of a Contact. It shows basic details (name, address etc.), a list of known email addresses and a list of telephone numbers and allows them to add/edit email and telephone details. My view has three forms, one for the basic details, one for the email details and one for the telephone details - each one posts to a different controller action (SaveDetails, SaveEmail, SaveTelephone). The problem is if the user goes to add a new email and the ModelState is not valid then once I return to the main Details (HttpGet) controller method to show the page and the model state error then I have lost the data that the user entered into the email form.

I don't really want to have one big form containing all the details and I want to avoid using TempData to store/pass the form input back to the main page - does anyone have any suggestions for how to achieve this?

Here is my view model ContactDetailsViewModel which contains the EmailDetailsViewModel and TelephoneFaxDetailsViewModel:

public class ContactDetailsViewModel : MasterViewModel
{
    public int ContactId { get; set; }
    public int? EmailId { get; set; }
    public int? TelephoneFaxId { get; set; }

    [DisplayName("First Name")]
    [StringLength(30, ErrorMessage = "First Name cannot be more than 30 characters")]
    public string FirstName { get; set; }

    [DisplayName("Last Name")]
    [StringLength(30, ErrorMessage = "Last Name cannot be more than 30 characters")]
    public string LastName { get; set; }

    public IList<EmailSummary> EmailSummaries { get; set; }
    public IList<TelephoneFaxSummary> TelephoneFaxSummaries { get; set; }

    public EmailDetailsViewModel EmailDetails { get; set; }
    public TelephoneFaxDetailsViewModel TelFaxDetails { get; set; }
}

and my view which contains 3 separate forms and is strongly typed to the ContactDetailsViewModel:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<SPP.Portal.ViewModels.Contacts.ContactDetailsViewModel>" %>
<% using (Html.BeginForm("Details","Contact", FormMethod.Post))
   {%>
        <%: Html.ValidationSummary() %>
<fieldset>
    <%: Html.HiddenFor(m => m.EmailId) %>
    <%: Html.HiddenFor(m => m.TelephoneFaxId) %>

    <div class="editor-field">
        <div  class="left">
            <%: Html.LabelFor(model => model.FirstName) %>
            <%: Html.TextBoxFor(model => model.FirstName)%>
            <%: Html.ValidationMessageFor(model => model.FirstName)%>
        </div>
        <div class="right">
            <%: Html.LabelFor(model => model.LastName) %>
            <%: Html.TextBoxFor(model => model.LastName)%>
            <%: Html.ValidationMessageFor(model => model.LastName)%>
        </div>
    </div>

    <p>
        <input type="button" onclick="addEmail();" id="email" value="Add New Email" />
        <input type="button" onclick="addTelephoneFax();" id="telFax" value="Add New Telephone/Fax" />
        <input name="button" id="Save"type="submit" value="Save" />
        <input name="button" id="Return" type="submit" value="Return" />
    </p>

    <%
        } %>
    <fieldset>
        <legend>All Email Details</legend>
        <table>
            <tr>
                <th>Edit</th>
                <th>Email Address</th>
                <th>Email Type</th>
                <th>Delete</th>
            </tr>
            <% foreach (var emailSummary in Model.EmailSummaries)
               { %>
            <tr>
                <%: Html.Hidden(emailSummary.EmailId.ToString()) %>
                <td>
                   <a href="<%: ResolveUrl(emailSummary.EditUrl.OriginalString) %>">Edit</a>
                </td>
                <td>
                    <%: emailSummary.EmailAddress %>
                </td>
                <td>
                    <%: emailSummary.EmailType %>
                </td>
                <td>
                 <a href="<%: ResolveUrl(emailSummary.DeleteUrl.OriginalString) %>" class="confirmDelete">Delete</a>
                </td>
            </tr>
            <% } %>
        </table>
    </fieldset>

    <% using (Html.BeginForm("EmailDetails", "Contact", FormMethod.Post))
     {%>
    <div id="EmailDetails">
    <fieldset>
        <legend>Email</legend>

        <%: Html.HiddenFor(m => m.EmailDetails.Id)%>
        <%: Html.HiddenFor(m => m.EmailDetails.ContactId)%>
        <div class="editor-field">
            <div class="right">
                <%: Html.LabelFor(m => m.EmailDetails.EmailAddress)%>
                <%: Html.TextBoxFor(m => m.EmailDetails.EmailAddress)%>
                <%: Html.ValidationMessageFor(model => model.EmailDetails.EmailAddress)%>
            </div>
        </div>
        <div class="editor-field">
            <div class="left">
                <%: Html.LabelFor(m => m.EmailDetails.EmailType)%>
                <%: Html.DropDownListFor(m => m.EmailDetails.EmailType, Model.EmailDetails.EmailTypeCodes)%>
                <%: Html.ValidationMessageFor(model => model.EmailDetails.EmailType)%>
            </div>
        <p>
            <input type="submit" id="EmailSave" value="Save" />
            <input type="submit" name="button" id="EmailCancel" value="Cancel" />
        </p>
        </fieldset> 
    <% } %> 
    </div>

    <fieldset>
        <legend>All Telephone/Fax Details</legend>
        <table>
            <tr>
                <th>Edit</th>
                <th>Dialing Code</th>
                <th>Telephone/Fax Number</th>
                <th>Delete</th>
            </tr>
            <% foreach (var telFaxSummary in Model.TelephoneFaxSummaries)
               { %>
            <tr>
                <%: Html.Hidden(telFaxSummary.TelephoneFaxId.ToString())%>
                <td>
                   <a href="<%:ResolveUrl(telFaxSummary.EditUrl.OriginalString)%>" >Edit</a>
                </td>
                <td>
                    <%: telFaxSummary.DialingCode%>
                </td>
                <td>
                    <%: telFaxSummary.TelephoneFaxNumber%>
                </td>
                <td>
                  <a href="<%:ResolveUrl(telFaxSummary.DeleteUrl.OriginalString)%>" class="confirmDelete">Delete</a>
                </td>
            </tr>
            <% } %>
        </table>
    </fieldset>

    <% using (Html.BeginForm("TelephoneFaxDetails", "Contact", FormMethod.Post))
       {%>
        <div id="TelephoneFaxDetails">
        <%: Html.HiddenFor(m => m.TelFaxDetails.TelephoneFaxId)%>
        <%: Html.HiddenFor(m => m.TelFaxDetails.ContactId)%>
        <fieldset>
                <legend>Telephone/Fax</legend>
        <div class="editor-field">
            <div id="left" class="left">
                <%: Html.LabelFor(m => m.TelFaxDetails.InternationalDialingCode)%>
                <%: Html.TextBoxFor(m => m.TelFaxDetails.InternationalDialingCode)%>
                <%: Html.ValidationMessageFor(model => model.TelFaxDetails.InternationalDialingCode)%>
            </div>
            <div id="middle" class="right">
                <%: Html.LabelFor(m => m.TelFaxDetails.TelephoneFaxNumber)%>
                <%: Html.TextBoxFor(m => m.TelFaxDetails.TelephoneFaxNumber)%>
                <%: Html.ValidationMessageFor(model => model.TelFaxDetails.TelephoneFaxNumber)%>
            </div>
        </div>
            <p>
                <input type="submit" id="TelephoneSave" value="Save" />
                <input type="submit" name="button" id="TelephoneCancel" value="Cancel" />
            </p>
        </fieldset> 
    <% } %>
    </div>

And then my controller method to add a new Email:

    [HttpPost]
    public ActionResult EmailDetails(ContactDetailsViewModel contactViewModel)
    {
        var viewModel = contactViewModel.EmailDetails;

        if (ModelState.IsValid)
        {
            try
            {
                _viewModelService.UpdateEmail(User, viewModel);

                return RedirectToAction("Details");
            }
            catch (Exception ex)
            {
                ModelState.AddModelError("Exception", "The Email was not updated.");

                return RedirectToAction("Details");
            }
        }

        return View("Details", contactViewModel);
    }

So the idea is that if I take the whole ContactViewModel into the Email post then if the model is not valid then I should be able to return the whole Details ContactViewModel which would contain the basic contact details and the email details that the user had just input so they would not lose the data. The email details are passed to the controller fine however all of the basic contact data is null when passed into the controller - any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

指尖上的星空 2024-10-13 17:03:44

IMO,就您的情况而言,这是不可行的,我的意思是,当您发布第二个表格(电子邮件详细信息)时,您仅发布该表格,因此无论在其范围内,它都只会发送到服务器。

因此,在这种情况下,您可以选择 Ajax 来保存/验证单个字段集(电子邮件、所有电话/传真详细信息)或单个 ascx。

IMO, this will not be feasible as far as your scenario, I mean when you posting 2nd Form(EmailDetails) you only posting that FORM only, so what ever comes within the SCOPE of it,it only goes to SERVER.

So in such case, you could opt for Ajax to save/validate individual field set(Email,All Telephone/Fax Detail), or individual ascx.

铃予 2024-10-13 17:03:44

恐怕你的选择是有限的:

  • 你要么必须发布一个大的
    形式,这是保留的唯一选择
    所有价值观。
  • 或者你可以只拥有一个
    其中一次可编辑。但
    强制用户发帖三次
    可能不是你想要的。

一种可以让代码变得更好的方法是重新设计模型以具有三个属性。每个“表单”一个:

public class ContactDetailsViewModel
{
    public ContactDetails Basics { get; set; }
    public EmailDetails Email { get; set; }
    public PhoneDetails Phone { get; set; }
}

然后您可以为每个属性创建一个部分视图。或者自定义编辑器模板。这将使您的主要标记更加清晰。

Your options are limited, I am afraid:

  • You either have to post one large
    form, that's the only option to keep
    all values.
  • Or you can have only one
    of them editable at a time. But
    forcing the user to post three times
    is probably not what you want.

One approach, which will keep your code a little nicer, is to redesign the model to have three properties. One for each 'form':

public class ContactDetailsViewModel
{
    public ContactDetails Basics { get; set; }
    public EmailDetails Email { get; set; }
    public PhoneDetails Phone { get; set; }
}

You can then create a partial view for each property. Or a custom editor template. That will keep your main markup a little cleaner.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文