65.9K
CodeProject 正在变化。 阅读更多。
Home

.NET Core 7 Razor 页面:使用 Ajax POST 更新 Razor 页面的部分内容(局部更新),无需重新渲染整个内容

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2023 年 9 月 26 日

CPOL

2分钟阅读

viewsIcon

9437

在父 Razor 页面上,我们添加一个局部视图,它将作为可更新的容器。通过父页面的 Ajax 调用,我们仅更新局部视图的内容,而不是整个页面,从而创建一个流畅的用户交互体验。

引言

我在这个主题上花费了相当多的时间研究,但没有找到任何包含完整实现的资源。即使在 ChatGPT 的协助下,我也花了几天时间才得到一个最终的干净解决方案。

背景

再次说明,这是 Visual Studio 2022 中的 Razor 页面 Web 项目,但它也可以应用于 MVC,因为 MVC 具有相同的局部视图概念。

操作指南

我们将处理 Razor 页面 Web 项目的三个部分:Index.cshtmlIndex.cshtml.cs_SelectTable.cshtml 局部视图。这是一个最简化的 UI,没有任何花哨的东西,只是为了展示这个概念。请参考此文件夹结构以了解文件位置(我们感兴趣的文件在黄色中突出显示)。

局部视图 (_SelectTable.cshtml)

@model TestPartialViewWithAjax.Pages.IndexModel
@{
}

<table id="selectTable">
    <thead>
        <tr>
            <th>Select Element</th>
            <th>Action</th>
        </tr>
    </thead>
    <tbody>
        @if (Model.SelectedValues != null)
        {
            @for (var i = 0; i < Model.SelectedValues.Count; i++)
            {
                <tr>
                    <td>
                        <select asp-for="SelectedValues[i]">
                            @foreach (var item in Model.YourSelectList)
                            {
                                var selected = Model.SelectedValues[i] == item.Value ? 
                                               "selected" : "";
                                if (Model.SelectedValues[i] == item.Value)
                                {
                                    <option value="@item.Value" selected>
                                     @item.Text</option>
                                } else
                                {
                                    <option value="@item.Value">@item.Text</option>
                                }
                            }
                        </select>
                    </td>
                    <td>
                        <button type="button" class="deleteRowButton">Delete</button>
                    </td>
                </tr>
            }
        }
    </tbody>
</table>

在此页面中,我们渲染带有选择元素的表格行。添加新行时,现有行的选定选项将被保留。行也可以被删除,删除是通过父 cshtml 页面中的纯 JavaScript 块完成的。

父页面 (Index.cshtml)

@page
@using Newtonsoft.Json;
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">
       building Web apps with ASP.NET Core</a>.</p>
    <br /><br />
    <h3>List of Nominees</h3>
    <form method="post">
    <div id="selectTableContainer">
        <partial name="_SelectTable" model="Model" />
    </div>

    <button type="button" id="addRowButton">Add Row</button>
    <button type="submit">Submit</button>
    </form>
</div>

@section Scripts {
    <script>
        document.getElementById("addRowButton").addEventListener("click", function () {
            // Make an AJAX request to add a new row

            // Capture the user's current selections
            var selectedValues = [];
            $('select').each(function () {
                selectedValues.push($(this).val());
            });
            // Create a JavaScript object that combines both data
            var requestData = {
                YourSelectList: @Html.Raw
                (JsonConvert.SerializeObject(Model.YourSelectList)),
                SelectedValues: selectedValues
            };
            // Serialize the request data
            var serializedModel = JSON.stringify(requestData);
            
            $.ajax({
                url: '/?handler=AddRow',
                method: "POST",
                contentType: "application/json; charset=utf-8",
                headers: {
                    RequestVerificationToken:
                        $('input:hidden[name="__RequestVerificationToken"]').val()
                },
                data: JSON.stringify(serializedModel),
                success: function (result) {
                    $("#selectTableContainer").html(result);
                },
                error: function () {
                    alert("Failed to add a new row.");
                }
            });
        });

        // Handle row deletion
        $("#selectTableContainer").on("click", ".deleteRowButton", function () {
            var row = $(this).closest("tr");
            row.remove();
        });
    </script>
}

我们在 <form> 中包含 <partial name="_SelectTable" model="Model" /> 局部视图,我们在其中添加新的表格行。局部视图共享父页面的 Model。DeleteRow 方法使用纯 JavaScript,并简单地操作 HTML DOM 以从表单中删除字段。AddRowButton 点击使用 Ajax 调用到 AddRow 方法后面的页面。请注意,此 post 调用不会重新渲染页面,它仅更新局部视图部分。

提交 按钮的类型为“submit”,因此它会回发并刷新/重定向整个页面。它提交整个表格的选择数组,以便父页面知道来自局部视图的所有用户交互。

在这里,您可以添加 jquery datatable 插件,以获得花哨的表格功能,例如排序、搜索、编号、拖放等。

父页面后端 (Index.cshtml.cs)

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Newtonsoft.Json;

namespace TestPartialViewWithAjax.Pages
{
    public class IndexModel : PageModel
    {
        private readonly ILogger<IndexModel> _logger;
        [BindProperty]
        public List<string> SelectedValues { get; set; }= new List<string>();
        public List<SelectListItem> YourSelectList { get; set; }
        public IndexModel(ILogger<IndexModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
            // Initialize the YourSelectList with your options
            YourSelectList = new List<SelectListItem>
            {
                new SelectListItem { Value = "Default", Text = "Default",  
                                     Disabled = false,Group = null,Selected = false },
                new SelectListItem { Value = "Option1", Text = "Option 1",  
                                     Disabled = false,Group = null,Selected = false },
                new SelectListItem { Value = "Option2", Text = "Option 2",  
                                     Disabled = false,Group = null,Selected = false },
                new SelectListItem { Value = "Option3", Text = "Option 3",  
                                     Disabled = false,Group = null,Selected = false }
            };
            SelectedValues.Add("Default");
        }

        public IActionResult OnPost()
        {
          // breakpoint here to check SelectedValues  binding
          return RedirectToPage("./Index");
        }

        public IActionResult OnPostAddRow([FromBody] string serializedModel)
        {
            // Deserialize the model
            var model = JsonConvert.DeserializeObject<IndexModel>(serializedModel);

            // Add a new item to the SelectedValues list
            model.SelectedValues ??= new List<string>();
            model.SelectedValues.Add("Default");

            // Return the partial view with the updated model
            return Partial("_SelectTable", model);
        }
    }
}

在这里,OnPostAddRow 方法只是将一个新项目添加到列表中,并将更新后的局部视图 HTML 代码返回到 DOM,以在主表单中添加更多字段。

请注意,为了避免某些开发人员的困惑,当从 Ajax 调用 OnPostAddRow 方法时,IndexModel 类将作为新的实例初始化,所有默认/空属性都将被初始化。这里不会发生任何绑定([BindProperty] 被忽略),这就是为什么我们提供序列化的数据来自父页面以将其发送到局部视图。

结论

代码是从可用的 VS 2022 解决方案中粘贴的,应该可以随时运行/测试/审查/调试。成功编译可能需要的唯一一件事是添加 Newtonsoft.Json 包(如果 VS 2022 没有自动为你执行此操作)。

历史

  • 2023 年 9 月 26 日:初始版本
© . All rights reserved.