Entity Framework Core环境下jQuery DataTable无状态丢失更新方案咨询
我有一个基于.NET Core开发的应用,用到了Razor Pages和Entity Framework 5.0,用1.10.21版本的jQuery DataTable在局部页展示数据库数据。
我做了一个Add按钮,点击后会弹出Bootstrap 4.3.1版本的模态框让用户输入新对象的信息。提交成功后,新的Student对象会通过EF持久化到数据库,JavaScript会在Index页弹出通知,然后尝试更新DataTable加载最新数据。
目前我能用的DataTable刷新方式只有两种:
- 调用
window.location重载整个页面 - 关闭模态框的jQuery代码请求承载DataTable的局部页的OnGet接口,用返回的更新后div替换原有div,因为DOM发生了变化,需要重新实例化DataTable
第二种方式基本能满足需求,不用重载整个页面,但因为要重新实例化DataTable,原来的筛选、排序状态都会被清空。
我知道DataTable有add()方法可以新增行,但所有示例都是用JSON作为参数,不支持直接传入EF对象。我后续还要实现行的删除、编辑功能,也会遇到同样的问题:更新DataTable的时候没法保留原有状态。
应@Rena要求,我附上了_StudentModalPartial.cshtml的代码,同时针对我尝试过的其他方案提出两个明确问题:
- 通过isValid校验后,我可以不替换DataTable所在的div,而是返回构造好的
<tr>元素添加到DataTable中。我知道这个方案可行,但没尝试实现,因为这个方案不够合理:虽然可以避免重新实例化DataTable、保留状态,但手动构造包含所有列、渲染后按钮和数据的<tr>非常繁琐,我实际的业务比这个学生示例复杂很多,维护难度很高,而且也解决不了删除和编辑场景的问题。 - 目前我可以直接在HTML/DataTable中正常使用EF对象。如果调用
row.add()、row.remove()等方法时没法用HTML作为数据源(看起来确实是这样),那么处理EF对象的最佳方案是什么?我看过一篇教程需要写大量逻辑把EF对象转换成JSON才能适配表格,应该有更简洁的实现方式。
Index.cshtml代码
@page @model SuzRazorApp.Pages.Students.IndexModel @{ ViewData["Title"] = "Students"; } <h2>Students</h2> <div class="container-fluid pt-3 pb-3"> <!-- Button trigger modal --> <button type="button" class="btn btn-primary" data-toggle="ajax-modal" data-url="@Url.Page("Index", "StudentModalPartial" )"> Add <i class="fas fa-plus fa-sm"></i> </button> </div> <!-- Modal placeholder --> <div id="modal-placeholder"></div> <!-- Students table --> <partial name="_StudentsTablePartial" model="Model.Students" /> @section Scripts { <script> jQuery(document).ready(function () { jQuery('#example').DataTable({ stateSave: true, stateDuration: -1, columnDefs: [ { orderable: false, targets: 3 }, { searchable: false, targets: [2, 3] } ] }); }); jQuery(function () { jQuery('[data-toggle="tooltip"]').tooltip() }) </script> }
Index.cshtml.cs代码
namespace SuzRazorApp.Pages.Students { public class IndexModel : PageModel { private readonly SchoolContextContext _context; private readonly IConfiguration Configuration; public IndexModel(SchoolContextContext context, IConfiguration configuration) { _context = context; Configuration = configuration; } [BindProperty] public IList<Student> Students { get; set; } public async Task OnGetAsync() { Students = await _context.Students.AsNoTracking().ToListAsync(); } public async Task<IActionResult> OnGetStudentsTablePartial() { Students = await _context.Students.AsNoTracking().ToListAsync(); return Partial("_StudentsTablePartial", Students); } public PartialViewResult OnGetStudentModalPartial() { // 该接口返回携带空Student对象的_StudentModalPartial局部页 return Partial("_StudentModalPartial", new Student { }); } // 保存新增数据 public async Task<IActionResult> OnPostStudentModalPartialAsync(Student model) { if (ModelState.IsValid) // 页面有校验错误时不执行保存 { _context.Students.Add(model); await _context.SaveChangesAsync(); } // 重新返回模态框局部页,保留用户已填内容和校验错误(错误通过数据注解校验处理) return Partial("_StudentModalPartial", model); } } }
DataTable对应局部页(_StudentTablePartial.cshtml)代码
@model IList<Models.Student> <div id="divTable" class="container-fluid" data-url="@Url.Page("Index", "StudentsTablePartial")"> <table id="example" class="table table-sm table-bordered table-striped" style="width:100%" > <thead> <tr> <th> Last Name </th> <th> First Name </th> <th> Enrollment Date </th> <th></th> </tr> </thead> <tbody> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.LastName) </td> <td> @Html.DisplayFor(modelItem => item.FirstMidName) </td> <td> @Html.DisplayFor(modelItem => item.EnrollmentDate) </td> <td style="white-space:nowrap"> <a asp-page="./Edit" asp-route-id="@item.Id"> <i class="fas fa-pencil-alt" data-toggle="tooltip" title="Edit"></i> </a> <a asp-page="./Delete" asp-route-id="@item.Id"> <i class="far fa-trash-alt" data-toggle="tooltip" title="Delete"></i> </a> </td> </tr> } </tbody> </table> </div>
模态框保存后触发的site.js方法代码
// 按钮点击后在占位符加载模态框,为保存按钮绑定点击事件 $(function () { var placeholderElement = $('#modal-placeholder'); $('button[data-toggle="ajax-modal"]').click(function (event) { var url = $(this).data('url'); $.get(url).done(function (data) { placeholderElement.html(data); placeholderElement.find('.modal').modal('show'); }); }); placeholderElement.on('click', '[data-save="modal"]', function (event) { // 阻止按钮默认点击行为 event.preventDefault(); // 读取表单数据并提交 var form = $(this).parents('.modal').find('form'); var actionUrl = form.attr('action'); var dataToSend = form.serialize(); // 从表单action属性读取接口地址发起POST请求 $.post(actionUrl, dataToSend).done(function (data) { // data是渲染后的_ContactModalPartial局部页内容 var newBody = $('.modal-body', data); // 替换当前模态框内容 placeholderElement.find('.modal-body').replaceWith(newBody); // 校验通过则关闭模态框 var isValid = newBody.find('[name="IsValid"]').val() === 'True'; if (isValid) { $('#notificationsPlaceholder').find("p").text('数据保存成功!'); $('#notificationsPlaceholder').show(); var divTableElement = $('#divTable'); var divTableUrl = divTableElement.data('url'); $.get(divTableUrl).done(function (data) { // data是接口返回的渲染后的表格局部页内容 //console.log(typeof data); // string //console.log(data); // AJAX请求返回的HTML内容 divTableElement.html(data); // 替换原有表格DOM $('#example').DataTable(); // DOM更新后需要重新初始化DataTable插件 }); placeholderElement.find('.modal').modal('hide'); } }); }); });
_StudentModalPartial.cshtml代码
@model SuzRazorApp.Models.Student <!-- Modal --> <div class="modal fade" id="add-student" tabindex="-1" role="dialog" aria-labelledby="addStudentLabel" aria-hidden="true"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="addStudentLabel">Add Student</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <form asp-page-handler="StudentModalPartial"> <input name="IsValid" type="hidden" value="@ViewData.ModelState.IsValid.ToString()" /> <div class="form-group"> <label asp-for="FirstMidName"></label> <input asp-for="FirstMidName" class="form-control" /> <span asp-validation-for="FirstMidName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="LastName"></label> <input asp-for="LastName" class="form-control" /> <span asp-validation-for="LastName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="EnrollmentDate"></label> <input asp-for="EnrollmentDate" class="form-control" /> <span asp-validation-for="EnrollmentDate" class="text-danger"></span> </div> </form> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> <button type="button" class="btn btn-primary" data-save="modal">Save</button> </div> </div> </div> </div>
内容的提问来源于stack exchange,提问作者sldorman
相关产品推荐
相关产品推荐

