ASP.NET MVC 5 中的基于 AJAX 的 CRUD 操作的 Grid View





3.00/5 (8投票s)
在这篇文章中,我将解决这个问题,我们将学习如何在不刷新整个页面的情况下,使用 JQuery DataTables 在一页中实现 CRUD 操作。
引言
我经常在 **StackOverflow** 等在线论坛上看到这样的问题,提问者能够为他们的实体创建 CRUD 操作,这是一种正常的表单提交,但他们在通过 ajax 实现 CRUD 以便页面不完全刷新以获得更好的用户体验时会遇到困难。
在这篇文章中,我将解决这个问题,我们将学习如何在不刷新整个页面的情况下,使用 **JQuery DataTables** 在一页中实现 CRUD 操作。
背景
在之前的几篇文章中,我一直写到 **JQuery DataTables** 的用法,它可以被用来在 asp.net mvc 中构建类似 **GridView** 的功能,我们看到了如何使用 **NuGet 包管理器** 安装它并使用该插件实现一个简单的网格,该插件为我们提供了排序、搜索和分页等基本功能,然后我们看到了如何实现分页、过滤和排序,然后我们继续查看如何添加高级搜索以获得更好的用户体验。
如果有人想复习或有兴趣阅读相关的先前文章,可以在这里找到它们:
- ASP.NET MVC 5 创建 GridView 的初学者指南
- ASP.NET MVC 5 中带有服务器端过滤、排序和分页的 Grid View
- ASP.NET MVC 5 中使用 jQuery DataTables 进行服务器端高级搜索的 Grid View
- ASP.NET MVC 5 中的基于 AJAX 的 CRUD 操作的 Grid View
在这篇文章中,我们将学习如何为我们已经使用 JQuery DataTables 实现的网格添加创建、更新和删除操作支持。最终结果将看起来像这样
使用脚本创建数据库
首先,我们将运行脚本来创建数据库和表,这将是本文的关键。以下是脚本:
CREATE DATABASE [AdvancedSearchGridExampleMVC]
GO
CREATE TABLE [dbo].[FacilitySites] ([FacilitySiteID] UNIQUEIDENTIFIER NOT NULL,
[FacilityName] NVARCHAR (MAX) NULL,
[IsActive] BIT NOT NULL,
[CreatedBy] UNIQUEIDENTIFIER NOT NULL,
[CreatedAt] DATETIME NOT NULL,
[ModifiedBy] UNIQUEIDENTIFIER NULL,
[ModifiedAt] DATETIME NULL,
[IsDeleted] BIT NOT NULL
);
GO
CREATE TABLE [dbo].[Assets] (
[AssetID] UNIQUEIDENTIFIER NOT NULL,
[Barcode] NVARCHAR (MAX) NULL,
[SerialNumber] NVARCHAR (MAX) NULL,
[PMGuide] NVARCHAR (MAX) NULL,
[AstID] NVARCHAR (MAX) NOT NULL,
[ChildAsset] NVARCHAR (MAX) NULL,
[GeneralAssetDescription] NVARCHAR (MAX) NULL,
[SecondaryAssetDescription] NVARCHAR (MAX) NULL,
[Quantity] INT NOT NULL,
[Manufacturer] NVARCHAR (MAX) NULL,
[ModelNumber] NVARCHAR (MAX) NULL,
[Building] NVARCHAR (MAX) NULL,
[Floor] NVARCHAR (MAX) NULL,
[Corridor] NVARCHAR (MAX) NULL,
[RoomNo] NVARCHAR (MAX) NULL,
[MERNo] NVARCHAR (MAX) NULL,
[EquipSystem] NVARCHAR (MAX) NULL,
[Comments] NVARCHAR (MAX) NULL,
[Issued] BIT NOT NULL,
[FacilitySiteID] UNIQUEIDENTIFIER NOT NULL
);
GO
CREATE NONCLUSTERED INDEX [IX_FacilitySiteID]
ON [dbo].[Assets]([FacilitySiteID] ASC);
GO
ALTER TABLE [dbo].[Assets]
ADD CONSTRAINT [PK_dbo.Assets] PRIMARY KEY CLUSTERED ([AssetID] ASC);
GO
ALTER TABLE [dbo].[Assets]
ADD CONSTRAINT [FK_dbo.Assets_dbo.FacilitySites_FacilitySiteID] FOREIGN KEY ([FacilitySiteID]) _
REFERENCES [dbo].[FacilitySites] ([FacilitySiteID]) ON DELETE CASCADE;
GO
您可以在附件的源代码中找到名为 dbScript.sql 的文件,其中还包含用于快速入门的示例数据。运行它将创建数据库并在表中添加一些示例数据。
创建/插入操作
对于插入部分,首先我们需要在 Views >> Asset 中创建一个局部视图。右键单击 Asset 文件夹,然后导航到 Add >> MVC 5 Partial Page (Razor),如下图所示:
打开渲染 Asset 行的容器视图 _AssetsPartial.cshtml,该文件位于 Views >> Asset 目录中。添加用于“添加 Asset”按钮的 HTML,该按钮将打开一个弹出窗口,用于在数据库中插入新的 Asset 行。
<button type="button" class="btn btn-default btn-md" data-toggle="modal" data-url="@Url.Action("Create","Asset")" id="btnCreateAsset">
<span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> Add Asset
</button>
现在,打开 Asset 的 Index 视图,即 Views >> Asset >> index.csthtml。在脚本部分,为我们刚刚创建的按钮单击事件添加以下 jQuery 代码。它将调用 create get 操作,并将 Asset 的局部视图显示在 bootstrap modal 中供用户使用。代码如下:
$("#btnCreateAsset").on("click", function () {
var url = $(this).data("url");
$.get(url, function (data) {
$('#createAssetContainer').html(data);
$('#createAssetModal').modal('show');
});
});
我们还需要添加一个带有容器 div 的 bootstrap modal,我们将在其中加载 Asset 的 create 局部视图。因此,我们将以下 HTML 添加到 Index.cshtml 视图的末尾:
<div class="modal fade" id="createAssetModal" tabindex="-1" role="dialog" aria-labelledby="CreateAssetModal" aria-hidden="true" data-backdrop="static">
<div id="createAssetContainer">
</div>
</div>
实现 Add GET 操作
在 Asset 控制器文件(位于 Controllers >> AssetController.cs)中,为 Asset 创建的 GET 请求添加 Action 方法。上面的 jQuery 代码将调用此方法。在 Action 方法内部,我们填充 ViewModel 并将实例传回 PartialView,就像我们在 mvc 应用程序中通常做的那样。
public ActionResult Create()
{
var model = new AssetViewModel();
model.FacilitySitesSelectList = GetFacilitiySitesSelectList();
return View("_CreatePartial", model);
}
添加局部视图创建
在项目中的 Asset Views 中添加一个新的局部视图用于创建表单。为此,请在 Solution Explorer 中右键单击 Views 下的 Asset 文件夹。
在文本框中输入局部视图的名称,例如 _CreatePartial
,或者您认为更适合您的名称。
实现 Add 局部视图
现在我们将编写上一步中创建的局部视图 _CreatePatial
。在此视图中添加以下代码:
@model GridAjaxCRUDMVC.Models.AssetViewModel
@{
Layout = null;
}
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Add Asset</h4>
</div>
@using (Ajax.BeginForm("Create", "Asset", null, new AjaxOptions { HttpMethod = "Post", OnSuccess = "CreateAssetSuccess" }, new { @class = "form-horizontal", role = "form" }))
{
<div class="modal-body">
<div class="form-horizontal">
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.AssetID, new { Value = Guid.NewGuid() })
<div class="form-group">
@Html.LabelFor(model => model.Barcode, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Barcode, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Barcode, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.SerialNumber, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.SerialNumber, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.SerialNumber, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.FacilitySiteID, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(model => model.FacilitySiteID, Model.FacilitySitesSelectList, "Select One", new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.FacilitySiteID, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.PMGuide, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.PMGuide, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.PMGuide, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.AstID, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.AstID, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.AstID, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.ChildAsset, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.ChildAsset, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.ChildAsset, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.GeneralAssetDescription, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.GeneralAssetDescription, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.GeneralAssetDescription, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.SecondaryAssetDescription, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.SecondaryAssetDescription, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.SecondaryAssetDescription, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Quantity, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Quantity, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Quantity, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Manufacturer, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Manufacturer, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Manufacturer, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.ModelNumber, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.ModelNumber, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.ModelNumber, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Building, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Building, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Building, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Floor, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Floor, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Floor, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Corridor, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Corridor, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Corridor, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.RoomNo, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.RoomNo, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.RoomNo, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.MERNo, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.MERNo, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.MERNo, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.EquipSystem, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.EquipSystem, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.EquipSystem, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Comments, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Comments, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Comments, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Issued, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
@Html.EditorFor(model => model.Issued)
@Html.ValidationMessageFor(model => model.Issued, "", new { @class = "text-danger" })
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<input type="submit" class="btn btn-primary" value="Save" />
</div>
</div>
</div>
}
</div>
</div>
实现 Add/Create POST 操作
在视图代码中需要注意的重要部分是 Ajax.BeginForm
辅助部分,因为类名 Ajax
应该足以表明它将通过 Ajax 将模型发布回控制器操作,这意味着整个过程不会刷新,即在 asp.net web forms 方面不会发生完全的回发。
post 操作只是将 ViewModel 对象映射到 Model,然后将其保存在存储库中。
[HttpPost]
public async Task<ActionResult> Create(AssetViewModel assetVM)
{
if (!ModelState.IsValid)
return View("_CreatePartial", assetVM);
Asset asset = MaptoModel(assetVM);
DbContext.Assets.Add(asset);
var task = DbContext.SaveChangesAsync();
await task;
if (task.Exception != null)
{
ModelState.AddModelError("", "Unable to add the Asset");
return View("_CreatePartial", assetVM);
}
return Content("success");
}
我们正在尝试将新输入的 Asset 保存到数据库并跟踪其是否成功保存。如果成功保存,我们将返回一个字符串消息 **success** 给客户端。在 Ajax Form 的成功回调中,我们将检查操作是否成功,以便根据结果进行 UI 更改。
实现 Ajax 回调函数
我们可以看到在 BeginForm
辅助方法的参数中,我们指定了当 Ajax Form 成功从服务器返回时要调用的 JavaScript 函数,即 OnSuccess = <span style="color: #990000">"CreateAssetSuccess"</span>
。
现在让我们转到 Asset 的 index.cshtml 视图并定义成功回调实现:
function CreateAssetSuccess(data) {
if (data != "success") {
$('#createAssetContainer').html(data);
return;
}
$('#createAssetModal').modal('hide');
$('#createAssetContainer').html("");
assetListVM.refresh();
}
在这里,如果操作不成功,我们将更新客户端 HTML 以通知用户创建失败;如果插入成功,我们将关闭模态弹出窗口并刷新网格以显示最新信息。
编辑/更新操作
到目前为止,我们应该能够运行该应用程序并通过上面创建的 bootstrap modal 成功地通过 Ajax 将新的 Asset 添加到数据库中。现在让我们继续进行 Asset 的更新部分。
我们将数据表列集合中添加另一列,其中包含一个超链接,该链接将导航到 Edit 视图。但是,由于我们正在使用 Ajax 和 bootstrap modal,我们将以同样的方式进行更新,因此不会涉及重定向。让我们开始吧。
打开 Index.csthml 文件,并在 jQuery datatable 初始化中的列集合中添加一个新列。更新列数组后,我们的代码将如下所示:
"columns": [
{ "title": "Bar Code", "data": "BarCode", "searchable": true },
{ "title": "Manufacturer", "data": "Manufacturer", "searchable": true },
{ "title": "Model", "data": "ModelNumber", "searchable": true },
{ "title": "Building", "data": "Building", "searchable": true },
{ "title": "Room No", "data": "RoomNo" },
{ "title": "Quantity", "data": "Quantity" },
{
"title": "Actions",
"data": "AssetID",
"searchable": false,
"sortable": false,
"render": function (data, type, full, meta) {
return '<a href="@Url.Action("Edit","Asset")?id=' + data + '" class="editAsset">Edit</a>';
}
}
]
我们将新列的标题设置为显示 Actions 作为标题,并且我们将需要禁用该列的搜索和排序,因为它用于编辑操作,因此在此列上启用排序和搜索没有意义。接下来,我们定义列的渲染方法,并生成锚链接 HTML,该 HTML 可以调用 Asset 控制器的 Edit 操作,并将当前 Asset ID 传递过去以提取其信息并在 Edit 视图中显示。
定义 Edit/Update GET 操作
在进行 datatable js 更改后,现在我们需要创建一个 get action 方法,该方法将从数据库中检索 Asset 记录并将其显示给用户进行编辑,以弹出窗口的形式。
让我们实现它的 Edit get 操作:
public ActionResult Edit(Guid id) { var asset = DbContext.Assets.FirstOrDefault(x => x.AssetID == id); AssetViewModel assetViewModel = MapToViewModel(asset); if (Request.IsAjaxRequest()) return PartialView("_EditPartial",assetViewModel); return View(assetViewModel); }
该操作只是从数据库中检索行,在将其转换为 ViewModel 后,将其传回 partial view 进行渲染,或返回给客户端进行处理。由于不会发生回发,它将生成 HTML 并将 HTML 作为 ajax 调用的响应发送回来,客户端将处理该 HTML 并决定将其放在何处。
使用 JQuery 在客户端处理 Action 链接事件
从列渲染函数中,您可以看到锚链接上应用了一个名为 EditAsset
的类。这是因为 jQuery 事件处理程序将应用于锚链接,并且 Ajax 调用将发送到服务器。让我们在 Index 视图中定义该链接的事件处理程序:
$('#assets-data-table').on("click", ".editAsset", function (event) {
event.preventDefault();
var url = $(this).attr("href");
$.get(url, function (data) {
$('#editAssetContainer').html(data);
$('#editAssetModal').modal('show');
});
});
添加 Bootstrap Modal
现在将 bootstrap modal HTML 添加到 Index 视图中,该视图将作为加载 Edit 视图的占位符。将其定义在创建 bootstrap modal HTML 之后:
<div class="modal fade" id="editAssetModal" tabindex="-1" role="dialog" aria-labelledby="EditAssetModal" aria-hidden="true" data-backdrop="static">
<div id="editAssetContainer">
</div>
</div>
Edit/Update 局部视图创建
按照我们为添加 _CreatePartial.cshtml 所做的相同步骤,创建另一个新的局部视图,该视图用于创建 Asset。因此,在 Views 下的 Asset 文件夹中添加一个名为 _EditPartial.cshtml 的新局部视图,并在其中添加以下代码:
@model TA_UM.ViewModels.AssetViewModel
@{
Layout = null;
}
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Edit Asset</h4>
</div>
@using (Ajax.BeginForm("Edit", "Asset", null, new AjaxOptions { HttpMethod="Post", OnSuccess = "UpdateAssetSuccess" }, new { @class = "form-horizontal", role = "form" }))
{
<div class="modal-body">
<div class="form-horizontal">
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.AssetID)
<div class="form-group">
@Html.LabelFor(model => model.Barcode, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Barcode, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Barcode, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.SerialNumber, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.SerialNumber, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.SerialNumber, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.FacilitySiteID, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(model => model.FacilitySiteID,Model.FacilitySitesSelectList, "Select One", new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.FacilitySiteID, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.PMGuide, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.PMGuide, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.PMGuide, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.AstID, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.AstID, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.AstID, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.ChildAsset, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.ChildAsset, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.ChildAsset, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.GeneralAssetDescription, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.GeneralAssetDescription, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.GeneralAssetDescription, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.SecondaryAssetDescription, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.SecondaryAssetDescription, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.SecondaryAssetDescription, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Quantity, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Quantity, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Quantity, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Manufacturer, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Manufacturer, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Manufacturer, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.ModelNumber, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.ModelNumber, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.ModelNumber, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Building, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Building, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Building, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Floor, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Floor, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Floor, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Corridor, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Corridor, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Corridor, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.RoomNo, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.RoomNo, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.RoomNo, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.MERNo, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.MERNo, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.MERNo, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.EquipSystem, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.EquipSystem, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.EquipSystem, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Comments, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Comments, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Comments, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Issued, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
@Html.EditorFor(model => model.Issued)
@Html.ValidationMessageFor(model => model.Issued, "", new { @class = "text-danger" })
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<input type="submit" class="btn btn-primary" value="Save changes"/>
</div>
</div>
</div>
}
</div>
</div>
Edit 视图与 Insert 视图非常相似,除了它将发布到不同的操作,该操作将负责处理特定 Asset 行的更新。
实现 Edit/Update POST 操作
现在让我们实现 Edit 的 post 操作:
[HttpPost]
public async Task<ActionResult> Edit(AssetViewModel assetVM)
{
assetVM.FacilitySitesSelectList = GetFacilitiySitesSelectList(assetVM.FacilitySiteID);
if (!ModelState.IsValid)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return View(Request.IsAjaxRequest() ? "_EditPartial" : "Edit", assetVM);
}
Asset asset = MaptoModel(assetVM);
DbContext.Assets.Attach(asset);
DbContext.Entry(asset).State = EntityState.Modified;
var task = DbContext.SaveChangesAsync();
await task;
if (task.Exception != null)
{
ModelState.AddModelError("", "Unable to update the Asset");
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return View(Request.IsAjaxRequest() ? "_EditPartial" : "Edit", assetVM);
}
if(Request.IsAjaxRequest())
{
return Content("success");
}
return RedirectToAction("Index");
}
处理 Update Ajax 成功回调
在 Index.cshtml 中,实现 OnSuccess
回调函数,该函数将在 Asset 更新成功后调用。在回调函数中,模态窗口将被关闭,表单将被清除,以便如果用户再次打开进行编辑,将获取新的 HTML 并将更新后的信息显示在视图中。当然,datatable 也将被刷新以显示最新的更新:
/**** Edit Asset Ajax Form CallBack ********/ function UpdateAssetSuccess(data) { if (data != "success") { $('#editAssetContainer').html(data); return; } $('#editAssetModal').modal('hide'); $('#editAssetContainer').html(""); assetListVM.refresh(); }
对于 details 和 delete 操作,将遵循相同的方法。让我们看看 delete 部分。首先,打开 Index 视图,让我们在上面定义编辑链接的渲染函数中添加 details 和 delete 操作的超链接:
"render": function (data, type, full, meta) {
return '<a href="@Url.Action("Edit","Asset")?id=' + data + '" class="editAsset">Edit</a> | <a href="@Url.Action("Details","Asset")?id=' + data + '">Details</a> | <a href="@Url.Action("Delete","Asset")?id=' + data + '">Delete</a>';
}
现在 Action 列将包含 Edit、Details 和 Delete Asset 的三个超链接。
检索和删除操作
在这一点上,我们应该能够看到插入和更新功能正常工作。现在我们将转向删除部分,看看如何实现 Assets 的删除。为此,我们需要向我们在列中生成的超链接添加类,这些列将出现链接。让我们先做这个。我们需要在 DataTables 初始化代码的 columns
数组的最后一个列中定义 render
属性,并且我们将定义列值的渲染方式:
"render": function (data, type, full, meta) {
return '<a href="@Url.Action("Edit","Asset")?id=' + data + '" class="editAsset">Edit</a> | <a href="@Url.Action("Details","Asset")?id=' + data + '" class="detailsAsset">Details</a> | <a href="@Url.Action("Delete","Asset")?id=' + data + '" class="deleteAsset">Delete</a>';
}
我们已将 detailsAsset
和 deleteAsset
类添加到相应的锚标签,以便我们可以使用 jQuery 绑定事件并执行一些逻辑来显示详情或删除特定 Asset。
完成上述步骤后,现在我们将编写事件来处理这两个超链接的单击事件。我们将需要编写以下代码来实现此目的:
$('#assets-data-table').on("click", ".detailsAsset", function (event) {
event.preventDefault();
var url = $(this).attr("href");
$.get(url, function (data) {
$('#detailsAssetContainer').html(data);
$('#detailsAssetModal').modal('show');
});
现在,由于 details 标签的 handler 已经就位,让我们转到视图部分并编写相应视图中所需的 razor 代码。因此,创建一个名为 _detailsPartial
的新局部视图,该视图将负责显示所选 Asset 的详细信息。为此,再次右键单击 Solution Explorer 中 Views 目录下的 Asset 文件夹,并执行与之前相同的步骤,然后以提到的名称创建局部视图,并在其中添加以下代码:
@model GridAdvancedSearchMVC.Models.AssetViewModel
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Asset Details</h4>
<hr/>
</div>
<div class="modal-body">
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Barcode)
</dt>
<dd>
@Html.DisplayFor(model => model.Barcode)
</dd>
<dt>
@Html.DisplayNameFor(model => model.SerialNumber)
</dt>
<dd>
@Html.DisplayFor(model => model.SerialNumber)
</dd>
<dt>
@Html.DisplayNameFor(model => model.FacilitySite)
</dt>
<dd>
@Html.DisplayFor(model => model.FacilitySite)
</dd>
<dt>
@Html.DisplayNameFor(model => model.PMGuide)
</dt>
<dd>
@Html.DisplayFor(model => model.PMGuide)
</dd>
<dt>
@Html.DisplayNameFor(model => model.AstID)
</dt>
<dd>
@Html.DisplayFor(model => model.AstID)
</dd>
<dt>
@Html.DisplayNameFor(model => model.ChildAsset)
</dt>
<dd>
@Html.DisplayFor(model => model.ChildAsset)
</dd>
<dt>
@Html.DisplayNameFor(model => model.GeneralAssetDescription)
</dt>
<dd>
@Html.DisplayFor(model => model.GeneralAssetDescription)
</dd>
<dt>
@Html.DisplayNameFor(model => model.SecondaryAssetDescription)
</dt>
<dd>
@Html.DisplayFor(model => model.SecondaryAssetDescription)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Quantity)
</dt>
<dd>
@Html.DisplayFor(model => model.Quantity)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Manufacturer)
</dt>
<dd>
@Html.DisplayFor(model => model.Manufacturer)
</dd>
<dt>
@Html.DisplayNameFor(model => model.ModelNumber)
</dt>
<dd>
@Html.DisplayFor(model => model.ModelNumber)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Building)
</dt>
<dd>
@Html.DisplayFor(model => model.Building)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Floor)
</dt>
<dd>
@Html.DisplayFor(model => model.Floor)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Corridor)
</dt>
<dd>
@Html.DisplayFor(model => model.Corridor)
</dd>
<dt>
@Html.DisplayNameFor(model => model.RoomNo)
</dt>
<dd>
@Html.DisplayFor(model => model.RoomNo)
</dd>
<dt>
@Html.DisplayNameFor(model => model.MERNo)
</dt>
<dd>
@Html.DisplayFor(model => model.MERNo)
</dd>
<dt>
@Html.DisplayNameFor(model => model.EquipSystem)
</dt>
<dd>
@Html.DisplayFor(model => model.EquipSystem)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Comments)
</dt>
<dd>
@Html.DisplayFor(model => model.Comments)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Issued)
</dt>
<dd>
@Html.DisplayFor(model => model.Issued)
</dd>
</dl>
</div>
</div>
</div>
Details GET 操作实现
现在是时候定义控制器操作代码了,该代码将从数据源检索 Asset 信息并将其传递回视图供用户显示。这是控制器操作的代码:
public async Task<ActionResult> Details(Guid id)
{
var asset = await DbContext.Assets.FirstOrDefaultAsync(x => x.AssetID == id);
var assetVM = MapToViewModel(asset);
if(Request.IsAjaxRequest())
return PartialView("_detailsPartial", assetVM);
return View(assetVM);
}
实现 Delete 操作
我们将添加 delete 的 GET action 方法,该方法将从存储库中获取特定的 Asset 并将其详细信息显示在弹出窗口中,用户可以在其中删除或取消。以下是 action 方法的代码:
public ActionResult Delete(Guid id)
{
var asset = DbContext.Assets.FirstOrDefault(x => x.AssetID == id);
AssetViewModel assetViewModel = MapToViewModel(asset);
if (Request.IsAjaxRequest())
return PartialView("_DeletePartial", assetViewModel);
return View(assetViewModel);
}
Delete 局部视图添加
我们现在将在解决方案中为 delete 部分添加另一个局部视图。因此,导航到 Solution Explorer 中的 Views >> Asset 文件夹,然后从右键单击 Asset 文件夹时出现的上下文菜单中,使用 Add >> MVC 5 Partial Page (Razor) 选项添加一个新视图,并将局部视图命名为 _DeletePartial
,并在其中添加以下代码:
@model GridAdvancedSearchMVC.Models.AssetViewModel
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Delete Asset</h4>
<h3>Are you sure you want to delete this?</h3>
</div>
<div class="modal-body">
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Barcode)
</dt>
<dd>
@Html.DisplayFor(model => model.Barcode)
</dd>
<dt>
@Html.DisplayNameFor(model => model.SerialNumber)
</dt>
<dd>
@Html.DisplayFor(model => model.SerialNumber)
</dd>
<dt>
@Html.DisplayNameFor(model => model.FacilitySite)
</dt>
<dd>
@Html.DisplayFor(model => model.FacilitySite)
</dd>
<dt>
@Html.DisplayNameFor(model => model.PMGuide)
</dt>
<dd>
@Html.DisplayFor(model => model.PMGuide)
</dd>
<dt>
@Html.DisplayNameFor(model => model.AstID)
</dt>
<dd>
@Html.DisplayFor(model => model.AstID)
</dd>
<dt>
@Html.DisplayNameFor(model => model.ChildAsset)
</dt>
<dd>
@Html.DisplayFor(model => model.ChildAsset)
</dd>
<dt>
@Html.DisplayNameFor(model => model.GeneralAssetDescription)
</dt>
<dd>
@Html.DisplayFor(model => model.GeneralAssetDescription)
</dd>
<dt>
@Html.DisplayNameFor(model => model.SecondaryAssetDescription)
</dt>
<dd>
@Html.DisplayFor(model => model.SecondaryAssetDescription)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Quantity)
</dt>
<dd>
@Html.DisplayFor(model => model.Quantity)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Manufacturer)
</dt>
<dd>
@Html.DisplayFor(model => model.Manufacturer)
</dd>
<dt>
@Html.DisplayNameFor(model => model.ModelNumber)
</dt>
<dd>
@Html.DisplayFor(model => model.ModelNumber)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Building)
</dt>
<dd>
@Html.DisplayFor(model => model.Building)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Floor)
</dt>
<dd>
@Html.DisplayFor(model => model.Floor)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Corridor)
</dt>
<dd>
@Html.DisplayFor(model => model.Corridor)
</dd>
<dt>
@Html.DisplayNameFor(model => model.RoomNo)
</dt>
<dd>
@Html.DisplayFor(model => model.RoomNo)
</dd>
<dt>
@Html.DisplayNameFor(model => model.MERNo)
</dt>
<dd>
@Html.DisplayFor(model => model.MERNo)
</dd>
<dt>
@Html.DisplayNameFor(model => model.EquipSystem)
</dt>
<dd>
@Html.DisplayFor(model => model.EquipSystem)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Comments)
</dt>
<dd>
@Html.DisplayFor(model => model.Comments)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Issued)
</dt>
<dd>
@Html.DisplayFor(model => model.Issued)
</dd>
</dl>
@using (Ajax.BeginForm("Delete", "Asset", null, new AjaxOptions { HttpMethod = "Post", OnSuccess = "DeleteAssetSuccess" }, new { @class = "form-horizontal", role = "form" }))
{
<div class="form-actions no-color">
@Html.HiddenFor(x => x.AssetID)
<input type="submit" value="Delete" class="btn btn-default" /> |
@Html.ActionLink("Back to List", "Index",null,new { data_dismiss = "modal" })
</div>
}
</div>
</div>
</div>
现在,我们需要为 delete 弹出窗口再次添加一个容器 div,该 div 将保存来自局部视图 _DeletePartial
的响应 HTML,就像我们为其他三个操作所做的那样。因此,在 Asset
的 Index.cshtml 视图中添加以下 HTML:
<div class="modal fade" id="deleteAssetModal" tabindex="-1" role="dialog" aria-labelledby="DeleteAssetModal" aria-hidden="true" data-backdrop="static">
<div id="deleteAssetContainer">
</div>
</div>
使用 JQuery 处理 Delete 链接事件
我们还需要实现 delete 按钮的单击事件,该事件将负责异步调用 action 方法并将响应 HTML 添加到弹出窗口容器中,然后将该弹出窗口容器显示给用户。代码如下:
$('#assets-data-table').on("click", ".deleteAsset", function (event) {
event.preventDefault();
var url = $(this).attr("href");
$.get(url, function (data) {
$('#deleteAssetContainer').html(data);
$('#deleteAssetModal').modal('show');
});
});
处理 DELETE Ajax POST 成功回调
如果您注意到这里,我们仍然使用 Ajax.BeginForm
辅助方法来发布需要删除的 Asset 行的 **GUID**。我们已指定了一个 JavaScript 成功回调,但我们尚未在 **Index** 视图中定义该函数。所以,让我们也这样做。这是函数定义:
function DeleteAssetSuccess(data) {
if (data != "success") {
$('#deleteAssetContainer').html(data);
return;
}
$('#deleteAssetModal').modal('hide');
$('#deleteAssetContainer').html("");
assetListVM.refresh();
}
这也与我们为其他操作所做的非常相似。我们正在确保,无论删除是否成功,我们都会相应地更新 UI。这就是上面的代码所做的。它关闭弹出窗口,清除容器 div 的 HTML,并刷新 datatable ViewModel 以反映网格中的最新更改。
实现 Delete POST 操作
最后,我们需要定义 delete 的 POST 操作,该操作将负责从数据库表中删除行,并返回操作的状态给视图(无论成功还是失败)。让我们这样做。这是 Delete
post 操作的代码:
[HttpPost, ActionName("Delete")]
public async Task<ActionResult> DeleteAsset(Guid AssetID)
{
var asset = new Asset { AssetID = AssetID };
DbContext.Assets.Attach(asset);
DbContext.Assets.Remove(asset);
var task = DbContext.SaveChangesAsync();
await task;
if (task.Exception != null)
{
ModelState.AddModelError("", "Unable to Delete the Asset");
Response.StatusCode = (int)HttpStatusCode.BadRequest;
AssetViewModel assetVM = MapToViewModel(asset);
return View(Request.IsAjaxRequest() ? "_DeletePartial" : "Delete", assetVM);
}
if (Request.IsAjaxRequest())
{
return Content("success");
}
return RedirectToAction("Index");
}
现在运行该应用程序,您应该能够通过 Ajax 添加、更新和删除行,而无需在页面之间导航。