在 ASP.NET MVC 5 中使用 JQuery DataTables 进行服务器端高级搜索的 Grid






4.92/5 (12投票s)
本文演示了如何在 asp.net mvc 5 中使用 JQuery DataTables 在 Grid 上实现高级搜索
背景
在前两篇关于在 ASP.NET MVC 中实现 GridView
的帖子中,我们讨论了如何使用 JQuery DataTables
插件创建一个类似于 ASP.NET Web Forms 中的 Grid,然后在第二篇帖子中,我们看到了如何通过实现排序、搜索和分页来提高 Grid 的性能,因为在第一篇帖子中我们实现了一个 Grid,但问题是页面首次加载时,所有行都会作为 HTML 呈现,并且过滤、分页和排序都是在客户端进行的,由 datatables 插件处理。
如果您有兴趣阅读这些内容,可以点击下面的链接找到这两篇帖子
- ASP.NET MVC 5 创建 GridView 的初学者指南
- ASP.NET MVC 5 中带有服务器端过滤、排序和分页的 Grid View
- 在 ASP.NET MVC 5 中使用 JQuery DataTables 进行服务器端高级搜索的 Grid
- ASP.NET MVC 5 中的基于 AJAX 的 CRUD 操作的 Grid View
我希望在阅读了关于 ASP.NET MVC 中 Grid 的前几篇文章后,您现在能够更好地在 ASP.NET MVC 中创建 Grid View,这对于大多数初学者来说是一件困难的事情,特别是对于那些来自 Web Forms 开发经验的人来说。
引言
在这篇文章中,我们将看到如何为我们的 GridView
添加高级搜索,以便用户在搜索 Grid 中的数据时获得更友好的搜索体验。
我们不会重复之前已经完成的步骤,包括数据库创建和插入示例数据,使用所需的 nuget 包设置新 Web 应用程序项目,如果您直接阅读本文,您可能需要至少查看最后一篇关于服务器端过滤的帖子,以便熟悉我们的工作,因此,正如所说,我们将重复使用相同的项目和代码,并继续向其中添加新的部分。
在前一篇文章的最后,我们有一个带有服务器端分页、过滤和排序的 Grid,在 Grid 中实现高级搜索功能后,我们的应用程序将看起来像
步骤 1 - 数据库创建
我们在之前的帖子中看到,我们只有一个 Assets
表,用于在 Grid
中显示记录,并且所有数据都存储在一个非规范化的表中,因此我们对 Assets
表的一个列进行了规范化,并创建了一个名为 FacilitySites
的 Lookup
表来演示如何使用 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
如果数据库创建成功,之后我们需要向表中插入一些记录,以便当我们在应用程序中查询时,可以在页面上看到一些显示内容,以检查各项是否正常工作。所以以下是相应的脚本
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem],
[Comments], [Issued], [FacilitySiteID])
VALUES (N'd37cc16b-3d13-4eba-8c98-0008b409a77b', N'D04-056',
N'N/A', N'D-04', N'D04-056', N'N/A',
N'DOOR, HYDR/ELEC/PNEUM OPERATED', N'N/A', 1, N'KM',
N'N/A', N'South', N'7', N'E', N'019',
N'', N'', N'Swing', 0,
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem],
[Comments], [Issued], [FacilitySiteID])
VALUES (N'7be68b37-5ec3-4a8b-be48-00490049f66b', N'C06-114',
N'N/A', N'C-06', N'C06-114', N'A11-13,C08-16',
N'CONTROLS, CENTRAL SYSTEM, HVAC', N'N/A', 1, N'N/A',
N'N/A', N'South', N'9', N'F', N'004',
N'MER5 ', N'AC-SE-2', N'rtn damper', 0,
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem], [Comments],
[Issued], [FacilitySiteID])
VALUES (N'e8a8af59-a863-4757-93bd-00561f36122b', N'C03-069',
N'N/A', N'C-03', N'C03-069', N'',
N'COILS, REHEAT/PREHEAT (REMOTE)', N'N/A', 1, N'N/A',
N'N/A', N'North', N'4', N'A', N'222',
N'', N' RH-N-17', N'', 0,
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem], [Comments],
[Issued], [FacilitySiteID]) VALUES (N'69dcdb07-8f60-4bbf-ad05-0078f3902c48',
N'D06-300', N'N/A', N'D-06', N'D06-300',
N'', N'DRAIN, AREAWAY/DRIVEWAY/STORM', N'N/A', 1,
N'N/A', N'N/A', N'South', N'Exterior', N'',
N'1s0?', N'SB areaway 1st', N'', N'', 0,
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber], [Building],
[Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem], [Comments], [Issued],
[FacilitySiteID]) VALUES (N'5b229566-5226-4e48-a6c7-008d435f81ae',
N'A05-46', N'N/A', N'A-05', N'A05-46', N'',
N'Air Conditioning Machine, Split System Chilled Water Coils',
N'10 Tons and Under', 1, N'Trane', N'N/A', N'South',
N'1', N'G', N'022', N'Headquarter Protective Force',
N'', N'Above Ceilg', 0, N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription], [SecondaryAssetDescription],
[Quantity], [Manufacturer], [ModelNumber], [Building], [Floor], [Corridor], [RoomNo],
[MERNo], [EquipSystem], [Comments], [Issued], [FacilitySiteID])
VALUES (N'108d1792-7aa1-4865-a3d3-00a0ea973aa3', N'C06-252',
N'N/A', N'C-06', N'C06-252', N'F27-35,C08-33',
N'CONTROLS, CENTRAL SYSTEM, HVAC', N'N/A', 1, N'N/A',
N'N/A', N'South', N'9', N'F', N'004',
N'MER5 ', N'E-SE-1', N'exh damper', 0,
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem], [Comments],
[Issued], [FacilitySiteID]) VALUES (N'80b9e4f9-71a4-4bd6-85c1-00a404cfee2b',
N'D06-409', N'N/A', N'D-06', N'D06-409', N'',
N'DRAIN, AREAWAY/DRIVEWAY/STORM', N'N/A', 1, N'N/A',
N'N/A', N'North', N'Exterior', N'',
N'eas0?', N'NB lawn east', N'', N'', 0,
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem],
[Comments], [Issued], [FacilitySiteID])
VALUES (N'bdad32e0-9c21-4451-8cc9-00b47b155eb9', N'D04-182',
N'N/A', N'D-04', N'D04-182', N'N/A', N'DOOR,
HYDR/ELEC/PNEUM OPERATED', N'N/A', 1, N'N/A', N'N/A',
N'South', N'2', N'E', N'2E-115',
N'Bathrooms', N'', N'HYDR/ELEC/PNEUM', 0,
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem],
[Comments], [Issued], [FacilitySiteID])
VALUES (N'4d859a1b-10e0-4cb0-96a4-00c164a7237e', N'C03-222',
N'N/A', N'C-03', N'C03-222', N'', N'COILS,
REHEAT/PREHEAT (REMOTE)', N'N/A', 1, N'N/A',
N'N/A', N'West', N'G', N'GJ, GI', N'086,052',
N'MER8 ', N'SW-26', N'', 0,
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem], [Comments],
[Issued], [FacilitySiteID]) VALUES (N'3df536d8-9f25-40dd-a83f-00c4434ad58e',
N'D06-348', N'N/A', N'D-06', N'D06-348',
N'', N'DRAIN, AREAWAY/DRIVEWAY/STORM', N'N/A', 1,
N'N/A', N'N/A', N'West', N'Exterior',
N'', N'2n4?', N'WB areaway 2nd', N'',
N'', 0, N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem],
[Comments], [Issued], [FacilitySiteID])
VALUES (N'26c671bc-47f1-4d0e-acc6-00cdfb94b67d', N'C06-165',
N'N/A', N'C-06', N'C06-165', N'A11-17,C08-22',
N'CONTROLS, CENTRAL SYSTEM, HVAC', N'N/A', 1, N'N/A',
N'N/A', N'South', N'9', N'F', N'004',
N'MER5 ', N'AC-SE-6', N'min OA', 0,
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem],
[Comments], [Issued], [FacilitySiteID])
VALUES (N'be09535a-0fb6-4f7b-a74e-00dab4730211', N'D04-034',
N'N/A', N'D-04', N'D04-034', N'N/A',
N'DOOR, HYDR/ELEC/PNEUM OPERATED', N'N/A', 1,
N'Dor-O-Matic, Jr', N'N/A', N'North', N'G',
N'A', N'064', N'', N'', N'Swing', 0,
N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode], [SerialNumber],
[PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem],
[Comments], [Issued], [FacilitySiteID])
VALUES (N'65a0abaa-75cf-489a-9367-0118486218b9', N'D05-049',
N'N/A', N'D-05', N'D05-049', N'N/A',
N'DOOR, ENTRANCE, MAIN', N'N/A', 1,
N'N/A', N'N/A', N'South', N'G_
1st', N'E', N'283', N'Ped Mall east', N'',
N'', 0, N'526fa0d5-1872-e611-b10e-005056c00008')
INSERT INTO [dbo].[Assets] ([AssetID], [Barcode],
[SerialNumber], [PMGuide], [AstID], [ChildAsset], [GeneralAssetDescription],
[SecondaryAssetDescription], [Quantity], [Manufacturer], [ModelNumber],
[Building], [Floor], [Corridor], [RoomNo], [MERNo], [EquipSystem],
[Comments], [Issued], [FacilitySiteID])
VALUES (N'c0101cf3-d1f1-4d32-a4b5-0135dc54645a',
N'C03-046', N'N/A', N'C-03', N'C03-046',
N'', N'COILS, REHEAT/PREHEAT (REMOTE)', N'N/A', 1,
N'N/A', N'N/A', N'North', N'5',
N'A', N'084', N'', N'RH-N-30',
N'', 0, N'526fa0d5-1872-e611-b10e-005056c00008')
GO
步骤 2 - 创建高级搜索表单
我们将为高级搜索创建一个新视图,其中将包含一个表单,该表单包含一些输入 HTML 控件,这些控件将被发布到控制器操作以过滤记录。
在解决方案资源管理器中,展开 Views 文件夹,然后再次展开 Asset 文件夹并打开 Index.cshtml 文件,我们将为 Grid 上方显示的高级搜索按钮添加 HTML。将以下 HTML 添加到视图中
<button type="button"
class="btn btn-default btn-md" data-toggle="modal"
data-target="#advancedSearchModal"
id="advancedsearch-button">
<span class="glyphicon glyphicon-search"
aria-hidden="true"></span> Advanced Search
</button>
如果您注意到,按钮代码中包含了一些新属性,您不必担心它们是用于 bootstrap 模态框的,因为点击按钮将打开一个模态对话框,用户可以在其中选择搜索条件并搜索结果。 data-toggle="modal"
属性决定此按钮将切换模态对话框,而 data-target="#advancedSearchModal"
指定页面上将显示为模态对话框的 HTML 元素。
在 Index.cshtml 中添加上述 HTML 代码后,视图将包含以下代码
<div class="row">
<div class="col-md-12">
<div class="panel panel-primary list-panel"
id="list-panel">
<div class="panel-heading list-panel-heading">
<h1 class="panel-title list-panel-title"
>Assets</h1>
<button type="button"
class="btn btn-default btn-md"
data-toggle="modal"
data-target="#advancedSearchModal"
id="advancedsearch-button">
<span class="glyphicon glyphicon-search"
aria-hidden="true"></span> Advanced Search
</button>
</div>
<div class="panel-body">
<table id="assets-data-table"
class="table table-striped table-bordered"
style="width:100%;">
</table>
</div>
</div>
</div>
</div>
@section Scripts
{
<script type="text/javascript">
var assetListVM;
$(function () {
assetListVM = {
dt: null,
init: function () {
dt = $('#assets-data-table').DataTable({
"serverSide": true,
"processing": true,
"ajax": {
"url":
"@Url.Action("Get","Asset")"
},
"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" }
],
"lengthMenu": [[10, 25, 50, 100], [10, 25, 50, 100]],
});
}
}
// initialize the datatables
assetListVM.init();
});
</script>
}
我们的模态弹出框最终将看起来像
步骤 3 - 使用 Entity Framework 添加模型
下一步是创建一个名为 FacilitySite
的新模型(DTO)类,该类将用于从上面数据库脚本创建的 FacilitySites
查找表中获取数据。因此,在解决方案资源管理器中 Models 文件夹中添加一个名为 FacilitySite
的新类,以下是其代码
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace GridAdvancedSearchMVC.Models
{
public class FacilitySite
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public System.Guid FacilitySiteID { get; set; }
[Display(Name = "Facility-Site")]
public string FacilityName { get; set; }
public bool IsActive { get; set; }
public System.Guid CreatedBy { get; set; }
[Required, DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreatedAt { get; set; }
public System.Guid? ModifiedBy { get; set; }
public DateTime? ModifiedAt { get; set; }
public bool IsDeleted { get; set; }
}
}
目前,我们只添加了 Model
类,它将保存 FacilitySites
表的数据,但由于我们使用 Entity Framework 进行数据访问,因此我们必须让它知道已添加了一个新表,可以在其上执行数据操作。为此,在 Models 文件夹中,打开 IdentityModel.cs 文件并将 ApplicationDbContext
代码更新为包含一个类型为 DbSet<FacilitySite>
的新属性
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public DbSet<Asset> Assets { get; set; }
public DbSet<FacilitySite> FacilitySites { get; set; }
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
同时更新 Asset
模型,删除之前类型为 String
的 FacilitySite
列,而是添加一个名为 FacilitySiteId
的新列,它将是 FacilitySite
表在 Asset
表中的外键,更新后的 Asset
模型应为
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace GridAdvancedSearchMVC.Models
{
public class Asset
{
public System.Guid AssetID { get; set; }
[Display(Name = "Barcode")]
public string Barcode { get; set; }
[Display(Name = "Serial-Number")]
public string SerialNumber { get; set; }
//[Display(Name = "Facility-Site")]
//public string FacilitySite { get; set; }
[ForeignKey("FacilitySite")]
public Guid FacilitySiteID { get; set; }
[Display(Name = "PM-Guide-ID")]
public string PMGuide { get; set; }
[Required]
[Display(Name = "Asset-ID")]
public string AstID { get; set; }
[Display(Name = "Child-Asset")]
public string ChildAsset { get; set; }
[Display(Name = "General-Asset-Description")]
public string GeneralAssetDescription { get; set; }
[Display(Name = "Secondary-Asset-Description")]
public string SecondaryAssetDescription { get; set; }
public int Quantity { get; set; }
[Display(Name = "Manufacturer")]
public string Manufacturer { get; set; }
[Display(Name = "Model-Number")]
public string ModelNumber { get; set; }
[Display(Name = "Main-Location (Building)")]
public string Building { get; set; }
[Display(Name = "Sub-Location 1 (Floor)")]
public string Floor { get; set; }
[Display(Name = "Sub-Location 2 (Corridor)")]
public string Corridor { get; set; }
[Display(Name = "Sub-Location 3 (Room No)")]
public string RoomNo { get; set; }
[Display(Name = "Sub-Location 4 (MER#)")]
public string MERNo { get; set; }
[Display(Name = "Sub-Location 5 (Equip/System)")]
public string EquipSystem { get; set; }
public string Comments { get; set; }
public bool Issued { get; set; }
public virtual FacilitySite FacilitySite { get; set; }
}
}
步骤 4 - 创建 ViewModel 类
我们还需要创建一个 ViewModel
类,用于将搜索条件发布到服务器端,以便控制器操作执行搜索。让我们然后添加 ViewModel
。以下是 AdvancedSearchViewModel
类中的代码
using System;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace GridExampleMVC.Models
{
public class AdvancedSearchViewModel
{
[Display(Name = "Facility-Site")]
public Guid FacilitySite { get; set; }
[Display(Name = "Main-Location (Building)")]
public string Building { get; set; }
public string Manufacturer { get; set; }
public string Status { get; set; }
public SelectList FacilitySiteList { get; set; }
public SelectList BuildingList { get; set; }
public SelectList ManufacturerList { get; set; }
public SelectList StatusList { get; set; }
}
}
步骤 5 - 实现高级搜索 Get 操作
导航到 Controllers 文件夹并展开它,然后打开 AssetController.cs 文件,我们将添加一个新的 get 操作,用于填充 AdvancedSeachViewModel
,我们将使用来自各自数据源的数据设置 SelectList
属性,以填充高级搜索模态弹出框上的下拉列表控件
[HttpGet]
public ActionResult AdvancedSearch()
{
var advancedSearchViewModel = new AdvancedSearchViewModel();
advancedSearchViewModel.FacilitySiteList = new SelectList(DbContext.FacilitySites
.Where(facilitySite => facilitySite.IsActive && !facilitySite.IsDeleted)
.Select(x => new { x.FacilitySiteID, x.FacilityName }),
"FacilitySiteID",
"FacilityName");
advancedSearchViewModel.BuildingList = new SelectList(DbContext.Assets
.GroupBy(x => x.Building)
.Where(x => x.Key != null && !x.Key.Equals(string.Empty))
.Select(x => new { Building = x.Key }),
"Building",
"Building");
advancedSearchViewModel.ManufacturerList = new SelectList(DbContext.Assets
.GroupBy(x => x.Manufacturer)
.Where(x => x.Key != null && !x.Key.Equals(string.Empty))
.Select(x => new { Manufacturer = x.Key }),
"Manufacturer",
"Manufacturer");
advancedSearchViewModel.StatusList = new SelectList(new List<SelectListItem>
{
new SelectListItem { Text="Issued",Value=bool.TrueString},
new SelectListItem { Text="Not Issued",Value = bool.FalseString}
},
"Value",
"Text"
);
return View("_AdvancedSearchPartial", advancedSearchViewModel);
}
步骤 6 - 实现高级搜索 Post 操作
我们的 AdvancedSearch
post 操作在实现上将与之前用于服务器端排序、过滤和分页的 Search 操作几乎相同,但 AdvancedSearch
的操作签名将有一个小变化,它现在将接受 2 个参数,这是很明显的,一个用于维护 DataTables
状态,这之前也是存在的,而新的参数将是 AdvancedSearchViewModel
类的一个实例,该实例将包含高级搜索模态弹出框控件的状态。
我们需要更新我们在上一篇关于 Grid View 服务器端处理的帖子中创建的 SearchAssets private
方法,将高级搜索数据库逻辑添加到此方法中,因此此方法将不再接受另一个参数,我们知道它是 AdvancedSearchViewModel
的实例
private IQueryable<Asset> SearchAssets(IDataTablesRequest requestModel,
AdvancedSearchViewModel searchViewModel, IQueryable<Asset> query)
{
// Apply filters
if (requestModel.Search.Value != string.Empty)
{
var value = requestModel.Search.Value.Trim();
query = query.Where(p => p.Barcode.Contains(value) ||
p.Manufacturer.Contains(value) ||
p.ModelNumber.Contains(value) ||
p.Building.Contains(value)
);
}
/***** Advanced Search Starts ******/
if (searchViewModel.FacilitySite != Guid.Empty)
query = query.Where(x => x.FacilitySiteID == searchViewModel.FacilitySite);
if (searchViewModel.Building != null)
query = query.Where(x => x.Building == searchViewModel.Building);
if (searchViewModel.Manufacturer != null)
query = query.Where(x => x.Manufacturer == searchViewModel.Manufacturer);
if (searchViewModel.Status != null)
{
bool Issued = bool.Parse(searchViewModel.Status);
query = query.Where(x => x.Issued == Issued);
}
/***** Advanced Search Ends ******/
var filteredCount = query.Count();
// Sort
var sortedColumns = requestModel.Columns.GetSortedColumns();
var orderByString = String.Empty;
foreach (var column in sortedColumns)
{
orderByString += orderByString != String.Empty ? "," : "";
orderByString += (column.Data) +
(column.SortDirection == Column.OrderDirection.Ascendant ?
" asc" : " desc");
}
query = query.OrderBy(orderByString ==
string.Empty ? "BarCode asc" : orderByString);
return query;
}
步骤 7 - 更新 DataTables Post 调用操作
现在也更新用于处理 Grid 服务器端处理的操作,使其能够接受高级搜索参数,并将它们传递给 SearchAssets
方法以进行更精细化的过滤,以下是该操作的更新代码
public ActionResult Get([ModelBinder(typeof(DataTablesBinder))]
IDataTablesRequest requestModel, AdvancedSearchViewModel searchViewModel)
{
IQueryable<Asset> query = DbContext.Assets;
var totalCount = query.Count();
// searching and sorting
query = SearchAssets(requestModel, searchViewModel,query);
var filteredCount = query.Count();
// Paging
query = query.Skip(requestModel.Start).Take(requestModel.Length);
var data = query.Select(asset => new
{
AssetID = asset.AssetID,
BarCode = asset.Barcode,
Manufacturer = asset.Manufacturer,
ModelNumber = asset.ModelNumber,
Building = asset.Building,
RoomNo = asset.RoomNo,
Quantity = asset.Quantity
}).ToList();
return Json(new DataTablesResponse
(requestModel.Draw, data, filteredCount, totalCount), JsonRequestBehavior.AllowGet);
}
步骤 8 - 实现高级搜索的模态弹出视图
现在,我们将转向视图部分,正如您所见,我们有最后四个 SelectList
类型的属性,因为我们将在高级表单中有几个下拉列表控件,用户将从中选择预先填充的值来搜索记录。
我们在 Index.cshtml 视图的 HTML 中添加的 data-target="#advancedSearchModal" 将在此部分视图中被引用,因此在 View >> Asset 下创建一个名为 _AdvancedSearchPartial
的新部分视图,为此,右键单击 View 下的 Asset 文件夹,然后导航到 Add Item,然后从下一个菜单中选择 MVC 5 Partial Page (Razor)
输入部分视图的名称,在这种情况下是 _AdvancedSearchPartial
,然后单击 OK 按钮
然后,打开文件 _AdvancedSearchPartial.cshtml,并在部分视图中添加 HTML,当用户单击我们在 Index.cshtml 视图中创建的高级搜索按钮时,将显示此 HTML 作为模态弹出框,以下是高级搜索部分视图的代码
@model TA_UM.ViewModels.AdvancedSearchViewModel
@{
Layout = null;
}
<div class="modal fade" id="advancedSearchModal"
tabindex="-1" role="dialog"
aria-labelledby="myModalLabel"
aria-hidden="true" data-backdrop="static">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Advanced Search</h4>
</div>
@using (Html.BeginForm("Get", "Asset",
FormMethod.Get, new { id = "frmAdvancedSearch",
@class = "form-horizontal", role = "form" }))
{
<div class="modal-body">
<div class="form-horizontal">
<hr />
<div class="form-group">
@Html.LabelFor(model => model.FacilitySite,
htmlAttributes: new { @class = "control-label col-md-3" })
<div class="col-md-8">
<div class="dropdown">
@Html.DropDownListFor(model => model.FacilitySite,
Model.FacilitySiteList, "Any", new { @class = "form-control" })
</div>
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Building,
htmlAttributes: new { @class = "control-label col-md-3" })
<div class="col-md-8">
<div class="dropdown">
@Html.DropDownListFor(model => model.Building,
Model.BuildingList, "Any", new { @class = "form-control" })
</div>
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Manufacturer,
htmlAttributes: new { @class = "control-label col-md-3" })
<div class="col-md-8">
<div class="dropdown">
@Html.DropDownListFor(model => model.Manufacturer,
Model.ManufacturerList, "Any", new { @class = "form-control" })
</div>
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Status,
htmlAttributes: new { @class = "control-label col-md-3" })
<div class="col-md-8">
<div class="dropdown">
@Html.DropDownListFor(model => model.Status,
Model.StatusList, "Both", new { @class = "form-control" })
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button id="btnPerformAdvancedSearch"
type="button" class="btn btn-default btn-success"
data-dismiss="modal">Search</button>
<button id="btnCancel" type="button"
class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
}
</div>
</div>
</div>
最后一步 - 从视图中的 POST 传递高级搜索参数
最后,打开位于 Views >> Asset 的 Index.cshtml 文件,并在 <span style="background-color: yellow">@section Scripts</span>
开始之前调用 AdvancedSearch
get 操作,以在浏览器中添加将显示在按钮触发时的高级搜索模态弹出 HTML,另一件需要注意的是,我们在任何地方都没有指定下拉列表选定的值将如何与 DataTables
服务器端处理在同一个操作中发布,尽管我们已经在操作中添加了参数,但我们没有在视图中进行任何特定更改,我们将不得不为此更新 jquery datatables 初始化代码,并使用 data 属性指定要发布到 AdvancedSearchViewModel
的值,为此我们需要定义属性,因此请在指定 datatable 的 URL 的行之后添加以下代码,即 "url": "@Url.Action("Get","Asset")",在添加完最后的 Index 视图后,代码应为
"data": function (data) {
data.FacilitySite = $("#FacilitySite").val();
data.Building = $("#Building").val();
data.Manufacturer = $("#Manufacturer").val();
data.Status = $("#Status").val();
}
我们的 Index 视图将包含以下代码
<div class="row">
<div class="col-md-12">
<div class="panel panel-primary list-panel" id="list-panel">
<div class="panel-heading list-panel-heading">
<h1 class="panel-title list-panel-title">Assets</h1>
<button type="button"
class="btn btn-default btn-md" data-toggle="modal"
data-target="#advancedSearchModal" id="advancedsearch-button">
<span class="glyphicon glyphicon-search"
aria-hidden="true"></span> Advanced Search
</button>
</div>
<div class="panel-body">
<table id="assets-data-table"
class="table table-striped table-bordered"
style="width:100%;">
</table>
</div>
</div>
</div>
</div>
@Html.Action("AdvancedSearch")
@section Scripts
{
<script type="text/javascript">
var assetListVM;
$(function () {
assetListVM = {
dt: null,
init: function () {
dt = $('#assets-data-table').DataTable({
"serverSide": true,
"processing": true,
"ajax": {
"url": "@Url.Action("Get","Asset")",
"data": function (data) {
data.FacilitySite = $("#FacilitySite").val();
data.Building = $("#Building").val();
data.Manufacturer = $("#Manufacturer").val();
data.Status = $("#Status").val();
}
},
"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" }
],
"lengthMenu": [[10, 25, 50, 100], [10, 25, 50, 100]],
});
},
refresh: function () {
dt.ajax.reload();
}
}
// Advanced Search Modal Search button click handler
$('#btnPerformAdvancedSearch').on("click", assetListVM.refresh);
}
// initialize the datatables
assetListVM.init();
});
</script>
}
您可以看到上面我们在 datatable
视图模型中添加了一个名为 refresh 的新函数,其目的是使用 DataTables
的模型从服务器端重新加载 datatable
,我们为高级搜索弹出按钮编写了事件处理程序,当按下该按钮时,它会导致 datatable
重新加载,并在其 Ajax 调用中,我们使用 jQuery datatables 的 data 属性传递用户从高级搜索视图中选择的搜索条件。
现在构建项目,并在浏览器中运行它,以查看正在工作的服务器端高级搜索,使用 JQuery datatables 以及服务器端过滤、分页和排序。