在前面的隨筆中,已經介紹了ABP的增刪改查的操做,可是對於查詢的數據並無進行分頁,只是進行粗糙的展現,今天的隨筆中將摸索進行分頁展現。這裏打算使用的分頁插件是DataTables,這是一款比較強大的表格插件。javascript
在之前咱們後臺手動分頁的時候,須要前臺傳入兩個重要的分頁參數:PageIndex和PageSize(顯示第幾頁的數據和每頁顯示的數量),這是必須的量的參數。分頁做爲一個頁面展現的基礎功能,ABP框架已經對分頁功能進行了一些方便性的操做,爲咱們提供了一些有助於分頁的接口和Dto,Dto是什麼?這個在前面的隨筆中已經研究過了,這裏就再也不重複。css
一 .ABP中的分頁接口html
在ABP中總共爲咱們提供了三個分頁的接口:IPagedResultRequest、ISortedResultRequest、ILimitedResultRequest三個接口java
從上面的三個接口中咱們看到了三個重要的變量,這就是咱們分頁和排序中常常用到的量。jquery
二. 實現分頁的Dtoajax
在咱們免費的ABP中模板中,也就是隻能找到這麼三個接口,對咱們分頁來講確實並無提供了多大的方便,可是在ABP Zero中已經對三個接口進行了相應的實現,只是zero是收費的。在這裏咱們能夠模仿zero在咱們ABP模板中添加上分頁的Dto,而且爲DataTables這個插件定製分頁Dto。json
詳細的代碼api
1 1.PagedInputDto 2 public class PagedInputDto : IPagedResultRequest 3 { 4 /// <summary> 5 /// 每頁顯示的行數 6 /// </summary> 7 [Range(1, AppConsts.MaxPageSize)] 8 public int MaxResultCount { get; set; } 9 /// <summary> 10 /// 跳過數量=MaxResultCount*頁數 11 /// </summary> 12 [Range(0, int.MaxValue)] 13 public int SkipCount { get; set; } 14 15 public PagedInputDto() 16 { 17 MaxResultCount = AppConsts.DefaultPageSize; 18 } 19 } 20 2. PagedAndFilteredInputDto 21 public class PagedAndFilteredInputDto : IPagedResultRequest 22 { 23 [Range(1, AppConsts.MaxPageSize)] 24 public int MaxResultCount { get; set; } 25 26 [Range(0, int.MaxValue)] 27 public int SkipCount { get; set; } 28 29 public string Filter { get; set; } 30 31 public PagedAndFilteredInputDto() 32 { 33 MaxResultCount = AppConsts.DefaultPageSize; 34 } 35 } 36 3. PageAndSortedInputDto 37 public class PagedAndSortedInputDto : PagedInputDto, ISortedResultRequest 38 { 39 public string Sorting { get; set; } 40 41 public PagedAndSortedInputDto() 42 { 43 MaxResultCount = AppConsts.DefaultPageSize; 44 } 45 } 46 4.PagedSortedAndFilteredInputDto 47 public class PagedSortedAndFilteredInputDto : PagedAndSortedInputDto 48 { 49 public string Filter { get; set; } 50 //接收DataTables的參數 51 public int Draw { get; set; } 52 public int Length 53 { 54 get 55 { 56 return this.MaxResultCount; 57 } 58 59 set 60 { 61 this.MaxResultCount = value; 62 } 63 } 64 public int Start 65 { 66 get 67 { 68 return this.SkipCount; 69 } 70 71 set 72 { 73 this.SkipCount = value; 74 } 75 } 76 } 77 5.DataTablesPageOutPutDto 78 [Serializable] 79 public class DataTablesPagedOutputDto<T>:PagedResultDto<T> 80 { 81 public int Draw { get; set; } 82 83 /// <summary> 84 /// 過濾後的記錄數(沒有就是所有),這個是必須的參數 85 /// </summary> 86 public int RecordsFiltered { get; set; } 87 88 public int RecordsTotal { get { return this.TotalCount; } } 89 90 public DataTablesPagedOutputDto(int totalCount, IReadOnlyList<T> items) 91 : base(totalCount, items) 92 { 93 this.RecordsFiltered = totalCount; 94 } 95 }
其中PagedSortedAndFilteredInputDto和DataTablesPageOutPutDto分別是爲了適應DataTables的需求定製的兩個類,Input的類中Start、Length、Draw、Filter都是爲了接收DataTables傳遞過來的參數,在OutPut類中定義了RecordsFiltered和recordsTotal和Draw這些都是DataTables須要的參數。說了這麼多,仍是先看一下DataTables這插件再說。緩存
三.DataTables分頁服務器
在這裏咱們使用的服務端分頁,詳細的內容可查看官網的具體介紹:http://www.datatables.club/manual/server-side.html
(1)Dto的請求參數
固然參數還有許多,可是主要的參數也就是上面的那幾個,尤爲是已經圈出來的這三個,就能夠完成分頁功能了,若是須要進行排序或者添加按照字段的搜索的功能,那麼就須要用到下面的字段了,咱們這裏只是使用分頁功能。
(2)Dto的返回參數
經過了上面DataTables官網的介紹,咱們已經清楚了咱們Dto中定義的參數的做用了,不知道你們有沒有一點困惑,就是Draw參數究竟是幹什麼的???哈哈哈,咱們在DataTables中已經找到了答案,他是防止跨站腳本攻擊的,關於他的賦值,只要給他賦值一個整數就能夠了。
三.在ABP中使用DataTables實現分頁
View
Index的具體代碼
@using Abp.Authorization.Users @using StudyABPProject.Web.Startup @model IList<StudyABPProject.Movie.Dto.MovieTicketDto> @{ ViewBag.CurrentPageName = PageNames.Movies; // The menu item will be active for this page. } @section scripts { <script src="~/lib/jquery-daterangepicker/daterangepicker.js" asp-append-version="true"></script> <script src="~/view-resources/Views/Movie/Index.js" asp-append-version="true"></script> <link href="~/lib/jquery-daterangepicker/daterangepicker.css" rel="stylesheet" /> <link href="~/lib/datatables/jquery.dataTables.min.css" rel="stylesheet" /> <script src="~/lib/datatables/jquery.dataTables.min.js"></script> } <div class="row clearfix"> <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12"> <div class="card"> <div class="header"> <h2> @L("Movie") </h2> <ul class="header-dropdown m-r--5"> <li class="dropdown"> <a href="javascript:void(0);" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"> <i class="material-icons">more_vert</i> </a> <ul class="dropdown-menu pull-right"> <li><a id="RefreshButton" href="javascript:void(0);" class="waves-effect waves-block"><i class="material-icons">refresh</i>Refresh</a></li> </ul> </li> </ul> </div> <div class="body table-responsive"> <button type="button" class="btn btn-primary waves-effect waves-float pull-right" data-toggle="modal" data-target="#MovieTicketCreateModal"> <i class="material-icons">添加</i> </button> <table id="MovieTable" name="MovieTable"></table> </div> </div> </div> </div> <div class="modal fade" id="MovieTicketCreateModal" tabindex="-1" role="dialog" aria-labelledby="UserCreateModalLabel" data-backdrop="static"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h4 class="modal-title"> <span>@L("CreateMovieTicket")</span> </h4> </div> <div class="modal-body"> <form name="movieCreateForm" role="form" novalidate class="form-validation"> <div class="tab-content"> <div role="tabpanel" class="tab-pane animated fadeIn active" id="create-user-details"> <div class="row clearfix" style="margin-top:10px;"> <div class="col-sm-12"> <div class="form-group form-float"> <div class="form-line"> <input class="form-control" type="text" name="MovieName" required maxlength="256" minlength="2"> <label class="form-label">@L("MovieName")</label> </div> </div> </div> </div> <div class="row clearfix"> <div class="col-sm-12"> <div class="form-group form-float"> <div class="form-line"> <input type="text" name="MovieActor" class="form-control" required maxlength="256"> <label class="form-label">@L("MovieActor")</label> </div> </div> </div> </div> <div class="row clearfix"> <div class="col-sm-12"> <div class="form-group form-float"> <div class="form-line"> <input type="datetime" name="StartTime" class="form-control" required> @*<label class="form-label">@L("StartTime")</label>*@ </div> </div> </div> </div> <div class="row clearfix"> <div class="col-sm-12"> <div class="form-group form-float"> <div class="form-line"> <input type="datetime" id="EndTime" name="EndTime" class="form-control"> @*<label class="form-label">@L("EndTime")</label>*@ </div> </div> </div> </div> <div class="row clearfix"> <div class="col-sm-12"> <div class="form-group form-float"> <div class="form-line"> <input type="number" id="Money" name="Money" class="form-control"> <label class="form-label">@L("Money")</label> </div> </div> </div> </div> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-default waves-effect" data-dismiss="modal">取消</button> <button type="submit" id="btnSave" class="btn btn-primary waves-effect">保存</button> </div> </form> </div> </div> </div> </div> <div class="modal fade" id="MovieTicketEditModal" tabindex="-1" role="dialog" data-backdrop="static"> <div class="modal-dialog" role="document"> <div class="modal-content"> </div> </div> </div>
Js
關於js代碼的位置我也按照框架中的位置放在了view-resources,話說Js代碼離View有點遠~~~
Index.Js中的主要代碼
(function () { $(function () { var _movieService = abp.services.app.movieTicket; var _$modal = $('#MovieTicketCreateModal'); var _$form = _$modal.find('form[name="movieCreateForm"]'); _$form.validate({ rules: { MovieName: { required:true }, StartTime: "required", EndtTime: { required: true }, MovieActor: "required" ,Money:"required" }, messages: { MovieName: { required:"電影名稱不能爲空" }, MovieActor: { required: "演員名稱不能爲空" }, StartTime: { required: "開始時間不能爲空" }, EndTime: { required: "結束時間不能爲空" }, Money: { required: "票價不能爲空" } } }); var dateOption = { locale: { format: 'YYYY-MM-DD HH:mm:ss', applyLabel: '確認', cancelLabel: '取消' }, singleDatePicker: true, startDate: moment().format("YYYY-MM-DD HH:mm:ss"), timePicker24Hour: true, timePicker: true, autoApply: true, autoUpdateInput: true }; $('input[name=StartTime]').daterangepicker(dateOption); $('input[name=EndTime]').daterangepicker(dateOption); $('#RefreshButton').click(function () { refreshUserList(); }); $('.delete-movie').click(function () { var movieId = $(this).attr("data-movie-id"); var movieName = $(this).attr("data-movie-name"); abp.message.confirm( "刪除電影 '" + movieName + "'?", function (isConfirmed) { if (isConfirmed) { _movieService.deleteMovie({ "id": movieId, "movieName": movieName, }).done(function () { refreshMovieList(); }); } } ); }); $('.edit-movie').click(function (e) { var movieId = $(this).attr("data-movie-id"); e.preventDefault(); $.ajax({ url: abp.appPath + 'MovieTicket/EditMovieTicketModal?movieId=' + movieId, type: 'POST', contentType: 'application/html', success: function (content) { $('#MovieTicketEditModal div.modal-content').html(content); }, error: function (e) { } }); }); _$form.find('button[type="submit"]').click(function (e) { e.preventDefault(); if (!_$form.valid()) { return; } var movie = _$form.serializeFormToObject(); abp.ui.setBusy(_$modal); _movieService.createMovie(movie).done(function (response) { if (response == "No") { abp.message.error("建立失敗"); } else { _$modal.modal('hide'); location.reload(true); } }).always(function () { abp.ui.clearBusy(_$modal); }); }); _$modal.on('shown.bs.modal', function () { _$modal.find('input:not([type=hidden]):first').focus(); }); function refreshMovieList() { location.reload(true); //reload page to see new user! } function deleteUser(userId, userName) { } var CONSTANT = { DATA_TABLES: { DEFAULT_OPTION: { //DataTables初始化選項 language: { "sProcessing": "處理中...", "sLengthMenu": "每頁 _MENU_ 項", "sZeroRecords": "沒有匹配結果", "sInfo": "當前顯示第 _START_ 至 _END_ 項,共 _TOTAL_ 項。", "sInfoEmpty": "當前顯示第 0 至 0 項,共 0 項", "sInfoFiltered": "(由 _MAX_ 項結果過濾)", "sInfoPostFix": "", "sSearch": "搜索:", "sUrl": "", "sEmptyTable": "表中數據爲空", "sLoadingRecords": "載入中...", "sInfoThousands": ",", "oPaginate": { "sFirst": "首頁", "sPrevious": "上頁", "sNext": "下頁", "sLast": "末頁", "sJump": "跳轉" }, "oAria": { "sSortAscending": ": 以升序排列此列", "sSortDescending": ": 以降序排列此列" } }, autoWidth: false, //禁用自動調整列寬 stripeClasses: ["odd", "even"],//爲奇偶行加上樣式,兼容不支持CSS僞類的場合 order: [], //取消默認排序查詢,不然複選框一列會出現小箭頭 processing: false, //隱藏加載提示,自行處理 serverSide: true, //啓用服務器端分頁 searching: false //禁用原生搜索 }, COLUMN: { CHECKBOX: { //複選框單元格 className: "td-checkbox", orderable: false, width: "30px", data: null, render: function (data, type, row, meta) { return '<input type="checkbox" class="iCheck">'; } } }, RENDER: { //經常使用render能夠抽取出來,如日期時間、頭像等 ELLIPSIS: function (data, type, row, meta) { data = data || ""; return '<span title="' + data + '">' + data + '</span>'; } } } }; var getQueryCondition=function(data) { var param = {}; //組裝排序參數 if(data.order&&data.order.length && data.order[0]) { //組裝分頁參數 param.start = data.start; param.length = data.length; param.draw = data.draw; return param; } var page = { $table: $("#MovieTable"), $dataTable: null, initDataPicker: function () { var dataOption = { startDate: moment().startOf("month"), "maxDate": null, singleDatePicker: true }; var dataOption1 = { startDate: moment().endOf("month"), "maxDate": null, singleDatePicker: true }; $("#StartTime").WIMIDaterangepicker(dataOption); $("#EndTime").WIMIDaterangepicker(dataOption1); }, initTable: function () { if (!$.fn.DataTable.isDataTable("#MovieTable")) { page.$datatable = page.$table.DataTable($.extend(true, {}, CONSTANT.DATA_TABLES.DEFAULT_OPTION, { ajax: function (data, callback, settings) { //封裝請求參數 var param = getQueryCondition(data); $.ajax({ type: "GET", url: "/api/services/app/" + "movieTicket/getAllMovieTicketPage", cache: false, //禁用緩存 data: param, //傳入已封裝的參數 dataType: "json", success: function (response) { //封裝返回數據 var returnData = {}; returnData.draw = response.result.draw;//這裏直接自行返回了draw計數器,應該由後臺返回 returnData.recordsTotal = response.result.recordsFiltered;//總記錄數 returnData.recordsFiltered = response.result.recordsFiltered;//後臺不實現過濾功能,每次查詢均視做所有結果 returnData.data = response.result.items; //調用DataTables提供的callback方法,表明數據已封裝完成並傳回DataTables進行渲染 //此時的數據需確保正確無誤,異常判斷應在執行此回調前自行處理完畢 callback(returnData); }, error: function (XMLHttpRequest, textStatus, errorThrown) { alert("查詢失敗"); } }); }, "paging": true, //綁定數據 "columns": [ { "defaultContent": "", "title": "操做", "orderable": false, "width": "150px", "className": "text-center not-mobile", "createdCell": function (td, cellData, rowData, row, col) { var $actionContent = $("<div class='action-content'>"); $('<button class="btn btn-xs">修改</button>') .appendTo($actionContent) .click(function () { alert(rowData.startTime); console.log(rowData); }); $('<button class="btn btn-xs"> 刪除 </button>') .appendTo($actionContent) .click(function () { alert(rowData.id); }); $(td).append($actionContent); }
},
{
"data": "movieName",
"title": "電影名稱"
},
{
"data": "movieActor",
"title": "演員名稱",
"width": "120px",
},
{
"data": "startTime",
"title": "開始時間",
"render": function (data, type, full, meta) {
return moment(data).format("YYYY-MM-DD HH:mm:ss");
}
},
{
"data": "endTime",
"title": "結束時間",
"render": function (data, type, full, meta) {
return moment(data).format("YYYY-MM-DD HH:mm:ss");
}
},
{
"data": "money",
"title": "票價",
}
], }));//此處需調用api()方法,不然返回的是JQuery對象而不是DataTables的API對象 } else { page.$datatable.ajax.reload(); } }, init: function () { page.initTable(); } } page.init(); }); })();
在這裏須要感謝https://blog.csdn.net/u011072139/article/details/54312414?locationnum=10&fps=1,從這篇博客類借鑑了一些Jscript代碼。
注意的問題:
(1)在使用DataTables的時候,常常出現一個錯誤,錯誤的提示:沒有「length」,其實出現這個錯誤的緣由是沒有爲Data賦值,DataTables須要返回Data,而後它會自動計算length,因此只要將Data賦值並返回便可。
(2)returnData.data = response.result.items;從這行代碼中能夠看出,咱們返回的數據並非一個簡單的對象,不能直接訪問咱們在後臺傳出的屬性,多封裝了一層。
上面的代碼中只是實現了分頁的功能,關於刪除和修改並無從新實現。須要注意的是在DataTables盡心後臺訪問的時候的請求路徑url,url: "/api/services/app/" + "movieTicket/getAllMovieTicketPage",這的路徑並非具體的控制器中的方法,而是直接訪問的Application層的服務方法,這裏就涉及了ABP中動態的Js代理,還一個須要重點關注的是type必須是Get類型,不然是沒法找到訪問路徑的,這是由於在ABP中動態Js代理默認的請求方式是get。關於動態的Js代理在ABP中的應用這個將會在後面的隨筆去研究。
後臺主要的代碼
public async Task<PagedResultDto<MovieTicketDto>> GetAllMovieTicketPage(MovieInputDto input) { var query = movieTicketRepository.GetAll() ; var totalCount =await query.CountAsync(); var models =await query.OrderBy(input.Sorting).AsNoTracking().PageBy(input).ToListAsync(); if (models.Count()==0) { return new DataTablesPagedOutputDto<MovieTicketDto>(0, new List<MovieTicketDto>()); } var items = models.MapTo<List<MovieTicketDto>>(); return new DataTablesPagedOutputDto<MovieTicketDto>(totalCount,items); }
到此爲止,基本的分頁功能已經實現,下面看一下運行的效果吧
請求的數據:
返回的數據: