如何验证多个Ajax加载的部分视图?

时间:2018-05-30 17:21:18

标签: c# ajax asp.net-mvc razor

我有一个视图Contact,可以加载nCaller个部分视图,mChild个部分视图和一个{{} 1}}部分视图在文档准备好后通过Ajax加载。

我也可以添加和删除CallNoteCallers,因此这些数字不是静态的。

Children,删除了一些内容:

Contact.cshtml

我的JS脚本片段@using Birth_To_Five.ViewModels @model CallDetailViewModel <div class="container"> <ul class="nav nav-tabs"> <li class="active"><a href="#tab-1" role="tab" data-toggle="tab">Call Detail</a></li> @* Other tabs not shown here *@ </ul> <div class="tab-content"> <div role="tabpanel" class="tab-pane active" id="tab-1"> @using (Html.BeginForm("SubmitCallDetailsAsync", "Home", FormMethod.Post)) { <div class="well"> @Html.AntiForgeryToken() @Html.HiddenFor(m => m.Id) @Html.HiddenFor(m => m.CallThreadViewModel.Id) <span style="color: red"> @Html.ValidationSummary() </span> @* Call Details *@ <div class="row"> <fieldset> <legend>Call Details</legend> </fieldset> </div> <div class="row"> <div class="form-group"> @Html.LabelFor(m => m.EnteredByEmail, new { @class = "control-label" }) @Html.ValidationMessageFor(m => m.EnteredByEmail, "", new { @class = "text-danger" }) @Html.TextBoxFor(m => m.EnteredByEmail, new { @class = "form-control", placeholder = "Who took the call" }) </div> @* Other stuff *@ </div> @* Caller Details *@ <div class="row"> <fieldset> <legend>Callers</legend> </fieldset> </div> @* Render each existing caller. Each caller gets their own well to create a visual separation between them. *@ @foreach (var callerViewModel in Model.CallerViewModels) { <div class="progress" id="callerLoadingBar-@callerViewModel.Id" data-callerid="@callerViewModel.Id" data-calldetailid="@Model.Id"> <div class="progress-bar progress-bar-striped active" role="progressbar" style="width: 100%">Loading Caller...</div> </div> } <div id="newCaller"></div> <div class="row"> @* Button to search for and add a caller *@ </div> @* Children Details *@ <div class="row"> <fieldset> <legend>Children</legend> </fieldset> </div> @* Render each existing child. Each child gets their own well to create a visual separation between them. *@ @foreach (var childViewModel in Model.ChildViewModels) { <div class="progress" id="childLoadingBar-@childViewModel.Id" data-childid="@childViewModel.Id" data-calldetailid="@Model.Id"> <div class="progress-bar progress-bar-striped active" role="progressbar" style="width: 100%">Loading Child...</div> </div> } <div id="newChild"></div> <div class="row"> @* Button to search for and add a child *@ </div> <div class="progress" id="callNoteLoadingBar"> <div class="progress-bar progress-bar-striped active" role="progressbar" style="width: 100%">Loading Call Note...</div> </div> </div> <div class="row"> <div class="form-group"> <button class="btn btn-danger" type="reset">Reset</button> </div> <div class="form-group"> <button class="btn btn-primary" type="submit">Submit</button> </div> </div> } </div> </div> </div> @section scripts { @Scripts.Render("~/bundles/jqueryval") <script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script> @Scripts.Render("~/bundles/calldetailscripts") }

callDetailFunctions

我的$(document).ready(function () { getCallNote('#callNoteLoadingBar', $('#Id').val()); getAllCallers(); getAllChildren(); }); // function getAllWhatever(){ Foreach loading bar, addCaller/Child/CallNotePartialView(..., ..., ..., etc.); } function addWhateverPartialView(divToReplace, thingIWantId, callDetailId) { $.ajax({ url: '/Home/GetWhateverPartialViewAsync', data: { thingIWantId, callDetailId }, type: "GET", error: function (xmlHttpRequest, textStatus, errorThrown) { alert("Request: " + xmlHttpRequest.toString() + "\n\nStatus: " + textStatus + "\n\nError: " + errorThrown); }, success: function (data) { $(divToReplace).replaceWith(data); } }); } 中有HomeController方法:

SubmitCallDetailsAsync

发生的事情的要点是我有一个加载栏作为每个[HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> SubmitCallDetailsAsync(CallDetailViewModel callDetailViewModel) { using (var unitOfWork = new UnitOfWork(ApplicationDbContext)) { // Call Details var callDetailServices = new CallDetailServices(); await callDetailServices.AddOrUpdateCallDetailFromCallDetailViewModelAsync(callDetailViewModel, ModelState, unitOfWork); // Callers var callerServices = new CallerServices(); await callerServices.AddOrUpdateCallersFromCallDetailsViewModelAsync(callDetailViewModel, ModelState, unitOfWork); // Children var childServices = new ChildServices(); await childServices.AddOrUpdateChildrenFromCallDetailsViewModelAsync(callDetailViewModel, ModelState, unitOfWork); // Call Note var callNoteServices = new CallNoteServices(); await callNoteServices.AddOrUpdateCallNoteFromCallDetailsViewModelAsync(callDetailViewModel, ModelState, unitOfWork); // Check the model state (returns true if it's good, false otherwise. // Also spits out some debug text for me to tell me what broke the Model) if (!UtilityServices.CheckModelState(ModelState)) { callDetailViewModel.DirectionChoices = await unitOfWork.DirectionChoiceRepo.GetAllAsSelectListItemsAsNoTrackingAsync(); return View("Contact", callDetailViewModel); } await unitOfWork.CompleteAsync(); } return RedirectToAction("Index"); } CallerChild的占位符,然后当文档加载时去Call Note

我的问题是,当我提交$(document).ready()并遇到模型验证错误时,我会收到我的Contact.cshtml页面,该页面会重新加载所有ContactCallers,和Children,因此失去了所有的变化。

我应该/我可以做些什么来处理这种情况?

1 个答案:

答案 0 :(得分:0)

使用deloopkat's comment,我能够将其工作(大部分时间)

我更改了我的Contact页面以使用Ajax.BeginForm(),并在我要用部分视图结果替换的部分添加了partialCallDetail ID:

@using (Ajax.BeginForm("SubmitCallDetailsAsync", "Home", new AjaxOptions() {HttpMethod = "POST", UpdateTargetId = "partialCallDetail", OnSuccess = "onSuccess"})) @* <----------- Note the UpdateTargetId *@
{
    <div class="well">
        @Html.AntiForgeryToken()
        @Html.HiddenFor(m => m.Id)
        @Html.HiddenFor(m => m.CallThreadViewModel.Id)
        <span style="color: red">
            @Html.ValidationSummary()
        </span>
        @* Call Details *@
        <div id="partialCallDetail"> @* <------------------ This whole div gets replaced by the Submit function when the Model Validation fails *@
            @* All of the same stuff as before in my original post *@
        </div>
    </div>
    <div class="row">
        <div class="form-group">
            <button class="btn btn-danger" type="reset">Reset</button>
        </div>
        <div class="form-group">
            <button class="btn btn-primary" type="submit">Submit</button>
        </div>
    </div>
}

@section scripts
{
    <script>
        function onSuccess(data) {
            ///<summary>
            /// When the Ajax form is submitted, this function gets called with the return data.
            /// Determine if it contains a redirectUrl and go there if it does
            ///</summary>
            if (data.redirectUrl !== undefined) {
                window.location.replace(data.redirectUrl);
            }
        }
    </script>
}

我创建了一个单独的部分视图_PartialCallDetail,它可以在现场呈现每个CallerChildCallNote的PartailView,而不是通过Ajax调用函数$(document).ready()

...
@* Render each existing caller. Each caller gets thier own well to create a visual seperation between them. *@
@foreach (var callerViewModel in Model.CallerViewModels)
{
    Html.RenderPartial("_PartialCallerInfo", callerViewModel);
}
...etc.

然后我将提交功能更改为:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SubmitCallDetailsAsync(CallDetailViewModel callDetailViewModel)
{
    using (var unitOfWork = new UnitOfWork(ApplicationDbContext))
    {
        // Call Details
        var callDetailServices = new CallDetailServices();
        await callDetailServices.AddOrUpdateCallDetailFromCallDetailViewModelAsync(callDetailViewModel, ModelState, unitOfWork);

        // Callers
        var callerServices = new CallerServices();
        await callerServices.AddOrUpdateCallersFromCallDetailsViewModelAsync(callDetailViewModel, ModelState, unitOfWork);

        // Children
        var childServices = new ChildServices();
        await childServices.AddOrUpdateChildrenFromCallDetailsViewModelAsync(callDetailViewModel, ModelState, unitOfWork);

        // Call Note
        var callNoteServices = new CallNoteServices();
        await callNoteServices.AddOrUpdateCallNoteFromCallDetailsViewModelAsync(callDetailViewModel, ModelState, unitOfWork);

        // Check the model state
        if (!UtilityServices.CheckModelState(ModelState))
        {
            // Setup all drop downs
            callDetailViewModel.DirectionChoices =
                await unitOfWork.DirectionChoiceRepo.GetAllAsSelectListItemsAsNoTrackingAsync();

            foreach (var callerViewModel in callDetailViewModel.CallerViewModels)
            {
                await callerServices.SetupSelectListItemsAsync(callerViewModel, unitOfWork);
            }

            foreach (var childViewModel in callDetailViewModel.ChildViewModels)
            {
                childViewModel.SexChoices = await unitOfWork.SexChoiceRepo.GetAllAsSelectListItemsAsNoTrackingAsync();
            }

            // Return the ViewModel with Validation messages
            if (Request.IsAjaxRequest()) return PartialView("_PartialCallDetail", callDetailViewModel);
            return View("Contact", callDetailViewModel);
        }

        await unitOfWork.CompleteAsync();
    }

    return Json(new { redirectUrl = Url.Action("Index", "Home", null) });
}

现在,如果出现模型验证错误,我会发送回_PartialCallDetail视图,该视图会使用现有数据更新Contact页面并激活@Html.ValidationMessageFor(...)

我认为重要的是要注意这在当前状态下并不完美:

  • 我现在有两个视图,Contact_PartialCallDetail,如果我将来进行设计更改,我需要更新。
  • 我在View中的@Html.ValidationSummary()仅适用于非Ajax内容,因此用户必须筛选视图以查看错误。
  • 还有其他几个特定于我的代码问题,但我会把它们排除在外。

但我觉得这是朝着正确方向迈出的一步