Asp.Net MVC 分页、检索、排序整体实现代码
很多时候需要这样的功能,对表格进行分页、排序和检索。这个有很多实现的方式,有现成的表格控件、用前端的mvvm,用户控件。但很多时候看着很漂亮的东西你想进一步控制的时候却不那么如意。这里自己实现一次,功能不是高大全,但求一个清楚明白,也欢迎园友拍砖。前端是bootstrap3+jpaginate,后台基于membership。没什么难点。
先上效果图。
分页其实就是处理好 每页项目数、总项目数、总页数、当前页。为了方便复用,就先从仓库开始说起。
一、建立仓库
1.定义ipager接口,需要分页的模型仓库继承这个接口
namespace protal.model.abstract { /// <summary> /// 分页处理 /// </summary> public interface ipager { /// <summary> /// 每页项目数 /// </summary> /// <value>the page item count.</value> int pageitemcount { get; set; } /// <summary> /// 总页数 /// </summary> /// <value>the totoal page.</value> int totoalpage { get; } /// <summary> /// 显示的页数 /// </summary> /// <value>the display page.</value> int displaypage { get; set; } /// <summary> /// 满足条件的总数目 /// </summary> int totalitem { get; set; } } }
2.定义iusersrepository,主要处理user 相关的业务逻辑。find函数是主要的查询方法,order表示顺反排序。
public interface iusersrepository : ipager { /// <summary> /// post list /// </summary> /// <param name="order">order expression</param> /// <param name="filter">filter expression</param> /// <param name="skip">records to skip</param> /// <param name="take">records to take</param> /// <returns>list of users</returns> ienumerable<user> find(int order=0,string filter="", int skip = 0, int take = 10); /// <summary> /// get single post /// </summary> /// <param name="name">user id</param> /// <returns>user object</returns> user findbyname(string name); /// <summary> /// add new user /// </summary> /// <param name="user">blog user</param> /// <returns>saved user</returns> user add(user user); /// <summary> /// update user /// </summary> /// <param name="user">user to update</param> /// <returns>true on success</returns> bool update(user user); /// <summary> /// save user profile /// </summary> /// <param name="user">blog user</param> /// <returns>true on success</returns> bool saveprofile(user user); /// <summary> /// delete user /// </summary> /// <param name="username">user id</param> /// <returns>true on success</returns> bool remove(string username); }
二、仓库的实现和绑定
主要方法:membership的中的user和我们自定义的不一样,所以存在一个转换
public class usersrepository : iusersrepository { /// <summary> /// the _user list /// </summary> private list<user> _userlist = new list<user>(); /// <summary> /// the _page item count /// </summary> private int _pageitemcount; /// <summary> /// the _display page /// </summary> private int _displaypage; /// <summary> /// the _usercount /// </summary> private int _usercount; /// <summary> /// the _total item /// </summary> private int _totalitem; /// <summary> /// 标记是否有查询条件 没有的话则返回全部数目 /// </summary> private func<user, bool> _func; /// <summary> /// gets or sets the users. /// </summary> /// <value>the users.</value> public list<user> users { get { int count; var usercollection = membership.getallusers(0, 999, out count); if (count == _usercount) return _userlist; _usercount = count; var members = usercollection.cast<membershipuser>().tolist(); foreach (var membershipuser in members)//这里存在一个转换 { _userlist.add(new user { email = membershipuser.email, username = membershipuser.username, //roles password }); } return _userlist; } set { _userlist = value; } } //查询 public ienumerable<user> find(int order = 0, string filter = "", int skip = 0, int take = 10) { if (take == 0) take = users.count; //过滤 _func = string.isnullorempty(filter) ? (func<user, bool>) (n => n.username != "") : (n => n.username.contains(filter)); var users = users.where(_func).tolist(); //更新总数目 _totalitem = users.count; users = order == 0 ? users.orderby(n => n.username).tolist() : users.orderbydescending(n => n.username).tolist(); return users.skip(skip).take(take); } /// <summary> /// 每页项目数 /// </summary> /// <value>the page item count.</value> public int pageitemcount { get { if (_pageitemcount == 0) { _pageitemcount = protalconfig.userpageitemcount; } return _pageitemcount; } set { _pageitemcount = value; } } /// <summary> /// 总页数 /// </summary> /// <value>the totoal page.</value> public int totoalpage { get { var page = (int) math.ceiling((double) totalitem/pageitemcount); return page==0?1:page; } } /// <summary> /// 显示的页数 /// </summary> /// <value>the display page.</value> public int displaypage { get { if (_displaypage == 0) { _displaypage = protalconfig.userdisplaypage; } return _displaypage; } set { _displaypage = value; } } /// <summary> /// 满足条件的总数目 保持更新 /// </summary> /// <value>the total item.</value> public int totalitem { get { if (_func == null) _totalitem = users.count; return _totalitem; } set { _totalitem = value; } } }
protalconfig.userdisplaypage 这里是通过配置实现一个默认页数,让用户可以再webconfig中更改行列的数目。
public static int userpageitemcount { get { if (_userpageitemcount == 0) { _userpageitemcount = webconfigurationmanager.appsettings["userpageitemcount"] != null ? convert.toint16(webconfigurationmanager.appsettings["userpageitemcount"]) : 5; } return _userpageitemcount; } set { _userpageitemcount = value; } }
再进行绑定:
_kernel.bind<iusersrepository>().to<usersrepository>();
三、控制器部分
我们需要两个页面,一个主页面index,一个负责局部刷新的部分视图 usertable
下面是主要的方法,主要逻辑都在在仓库中处理了。
[authorize] public class usermanagercontroller : controller { /// <summary> /// the _repository /// </summary> private readonly iusersrepository _repository; /// <summary> /// initializes a new instance of the <see cref="usermanagercontroller"/> class. /// </summary> /// <param name="irepository">the i repository.</param> public usermanagercontroller(iusersrepository irepository) { _repository = irepository; } /// <summary> /// indexes the specified page index. /// </summary> /// <param name="pageindex">index of the page.</param> /// <returns>actionresult.</returns> public actionresult index(int pageindex=1) { viewbag.displaypage = _repository.displaypage; pageindex = handlepageindex(pageindex); //支持地址栏直接分页 viewbag.currentpage = pageindex; return view(); } /// <summary> /// users table. 分页模块 /// </summary> /// <param name="pageindex">index of the page.</param> /// <param name="order">the order.</param> /// <param name="filter">the filter str.</param> /// <returns>actionresult.</returns> public actionresult usertable(int pageindex = 1, int order = 0, string filter = "") { pageindex = handlepageindex(pageindex); var skip = (pageindex - 1) * _repository.pageitemcount; var users = _repository.find(order,filter, skip, _repository.pageitemcount); //总用户数 viewbag.totaluser = _repository.totalitem; //总页数 viewbag.totalpagecount = _repository.totoalpage; ; return partialview(users); } /// <summary> /// 处理页数 防止过大或过小 /// </summary> /// <param name="index"></param> /// <returns></returns> private int handlepageindex(int index) { var totoalpage = _repository.totoalpage; if (index == 0) return 1; return index > totoalpage ? totoalpage : index; } }
四、视图部分html jquery
1.index.cshtml
<script src="~/scripts/form.js"></script> <div class="container"> <h4 class="bottomline">管理用户</h4> <p> <button data-target="#adduser" id="adduserbt" data-toggle="modal" class="btn btn-info btn-hover">新增用户</button> <button class="btn btn-danger" id="deluser">删除</button> <span class="errorinfo"></span> <input type="search" class="pull-right" id="usersearch" placeholder="搜索"/> </p> <div id="userpart"> @html.action("usertable",new{pageindex=viewbag.currentpage}) </div> <div id="userpager"></div> <input type="hidden" id="dispalypage" value="@viewbag.displaypage"/> <input type="hidden" id="page" value="@viewbag.currentpage"/> <input type="hidden" id="currentpage" value="@viewbag.currentpage"/> </div> <div class="modal fade adduserbox"id="adduser" tabindex="1" role="dialog" aria-hidden="true"> <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 new user</h4> </div> <div class="modal-body"> @{ html.renderaction("create","usermanager"); } </div> </div> </div> @section scripts { @scripts.render("~/bundles/jqueryval") }
2.usertable.cshtml,角色部分还未处理,这个表格更新之后,也会更新满足条件的用户数和新的总页数,触发jpaginate重新分页一次。
@model ienumerable<protal.model.data.user.user> <table id="usertable" class="table table-striped table-condensed table-hover table-bordered"> <tr> <th><input type="checkbox" id="allcheck" /><label for="allcheck">全选</label></th> <th><a href="#" id="usersort" data-order="0" class="glyphicon-sort">名称</a></th> <th>角色</th> <th>e-mail</th> </tr> <tbody> @foreach (var item in model) { <tr> <td> <input type="checkbox" data-id="@item.username" /></td> <td> <a>@item.username</a> </td> <td> @html.raw(item.role) </td> <td> @item.email</td> </tr> }</tbody> <tfoot> <tr> <td colspan="4"> <span>@html.raw("共"+viewbag.totaluser+"人")</span> @*<span>@viewbag.totalpagecount</span>*@ </td> </tr> </tfoot> </table> <input type="hidden" id="totoalpage" value="@viewbag.totalpagecount"/>
3.脚本
其中用到的像checkall,infoshow 都是自己扩展的一些简单的方法,用于全选和提示。
$(function () { var options = { datatype: 'json', success: processjson }; pageagin($("#totoalpage").val()); //分页 function pageagin(totalcount) { $("#userpager").paginate({ count: totalcount, start: $("#page").val(), dispaly: $("#dispalypage").val(), boder: false, border_color: '#fff',//自己调整样式。 text_color: 'black', background_color: 'none', border_hover_color: '#ccc', text_hover_color: '#000', background_hover_color: '#fff', images: false, mouse: 'press', onchange: function (page) { //翻页 paging(page); $("#currentpage").val(page); } }); } //分页更新 function paging(page) { $.post("/users/usertable", { pageindex: page, order: $("#userpart").attr("data-order"), filter: $.trim($("#usersearch").val()) }, function (data) { $("#userpart").html(data); }); } //排序 $("#usersort").live("click",function () { $("#userpart").triggerdataorder(); paging( $("#currentpage").val()); }); //搜索 $("#usersearch").keyup(function() { paging($("#currentpage").val()); pageagin($("#totoalpage").val()); }); //处理form $("#userform").submit(function () { $(this).ajaxsubmit(options); return false; }); function processjson(data) { if (data == 1) { location.reload(); } else { alert("添加失败"); } } //高亮 $("#unav li:eq(0)").addclass("active"); $("#adnav li:eq(2)").addclass("active"); //全选/全不选 $("#allcheck").checkall($("#usertable tbody input[type='checkbox']")); //删除用户 $("#deluser").click(function () { var checks = $("#usertable tbody input[type='checkbox']:checked"); var lens = checks.length; if (lens == 0) { $.infoshow("未选择删除对象",0); return false; } if (confirm("确定要删除所选中用户?")) { for (var i = 0; i < lens; i++) { var $chek = checks.eq(i); var id = $chek.attr("data-id"); var tr = $chek.parent().parent(); $.post("users/deleteuser", { id: id }, function (data) { if (data == 1) { tr.fadeout(); $.infoshow("删除成功", 1); } else { $.infoshow("删除失败", 0); } }); } } return true; }); // 增加用户 $("#adduserbt").click(function() { $(".modal-header").show(); }); })
到这里就是全部的代码,供大家和自己参考。
再给大家看两个效果图,一个是kendoui的grid,一个是angular做的分页。后面有机会给大家介绍。
kendo- grid
kendo和mvc框架融合度比较高,它的核心代码如下:
@model ienumerable<kendo.mvc.examples.models.productviewmodel> @(html.kendo().grid(model) .name("grid") .columns(columns => { columns.bound(p => p.productid).groupable(false); columns.bound(p => p.productname); columns.bound(p => p.unitprice); columns.bound(p => p.unitsinstock); }) .pageable() .sortable() .scrollable() .filterable() .datasource(datasource => datasource .ajax() .serveroperation(false) ) )
angularjs 核心还是调用封装好的api函数,相当于上面的仓库中的方法,然后通过模型绑定。
总结一下:自己实现代码量比较多,功能不全,有重复造*的感觉,但可以较好的控制,基本够用;kendo的方式感觉高大全,用熟了开发速度快。就是多一些引用,且需要担心kendoui和其他的ui框架会有冲突。前端mvvm的方式我了解还不够深,感觉前端脚本的代码量也蛮多,效果不错。但生成的html代码很少。上面这个表格。chrome f12或者右键查看源码都是下面这样子的:
主要的就一个div
<div data-ng-app="blogadmin" data-ng-view="" id="ng-view"></div>
自我保护倒是蛮好,也就是seo可能有问题。应该还有更好的方式,猿友们指点指点。
<html> <head> <title>name of the blog (admin)</title> <link rel="shortcut icon" href="/pics/blogengine.ico" type="image/x-icon" /> <meta charset="utf-8" /> <meta http-equiv="x-ua-compatible" content="ie=edge, chrome=1" /> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-status-bar-style" content="black" /> <meta name="format-detection" content="telephone=no" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <link href="/content/bootstrap/bootstrap.css" rel="stylesheet"/> <link href="/content/bootstrap/bootstrap-theme.css" rel="stylesheet"/> <link href="/content/toastr.css" rel="stylesheet"/> <link href="/content/font-awesome.css" rel="stylesheet"/> <link href="/content/editor.css" rel="stylesheet"/> <link href="/content/app.css" rel="stylesheet"/> <script type="text/javascript"> if (navigator.useragent.match(/iemobile\/10\.0/)) { var msviewportstyle = document.createelement("style"); var mq = "@-ms-viewport{width:auto!important}"; msviewportstyle.appendchild(document.createtextnode(mq)); document.getelementsbytagname("head")[0].appendchild(msviewportstyle); } </script> </head> <body> <script type="text/javascript"> var sitevars = { applicationrelativewebroot: '/', relativewebroot: '/', bloginstanceid: '96d5b379-7e1d-4dac-a6ba-1e50db561b04', username: 'admin', userrights: ['viewdetailederrormessages', 'accessadminpages', 'accessadminsettingspages', 'managewidgets', 'viewpubliccomments', 'viewunmoderatedcomments', 'createcomments', 'moderatecomments', 'viewpublicposts', 'viewunpublishedposts', 'createnewposts', 'editownposts', 'editotherusersposts', 'deleteownposts', 'deleteotherusersposts', 'publishownposts', 'publishotherusersposts', 'viewpublicpages', 'viewunpublishedpages', 'createnewpages', 'editownpages', 'viewratingsonposts', 'submitratingsonposts', 'viewroles', 'createnewroles', 'editroles', 'deleteroles', 'editownroles', 'editotherusersroles', 'createnewusers', 'deleteuserself', 'deleteusersotherthanself', 'editownuser', 'editotherusers'], absolutewebroot: 'http://localhost:53265/', version: 'blogengine.net ' + '2.9.1.0', isprimary: 'true', isadmin: 'true', approot: function (url) { window.location = '/' + url; return false; }, blogroot: function (url) { window.location = '/' + url; } }; </script> <script type="text/javascript" src="admin.res.axd"></script> <div id="container" class="app-wrapper ltr"> <div data-ng-app="blogadmin" data-ng-view="" id="ng-view"></div> </div> <script src="/scripts/jquery-2.0.3.js"></script> <script src="/scripts/jquery.validate.js"></script> <script src="/scripts/jquery.form.js"></script> <script src="/scripts/toastr.js"></script> <script src="/scripts/angular.min.js"></script> <script src="/scripts/angular-route.min.js"></script> <script src="/scripts/angular-animate.min.js"></script> <script src="/scripts/angular-sanitize.min.js"></script> <script src="/admin/be-grid.js"></script> <script src="/admin/app.js"></script> <script src="/admin/controllers/dashboard.js"></script> <script src="/admin/controllers/blogs.js"></script> <script src="/admin/controllers/posts.js"></script> <script src="/admin/controllers/pages.js"></script> <script src="/admin/controllers/tags.js"></script> <script src="/admin/controllers/categories.js"></script> <script src="/admin/controllers/comments.js"></script> <script src="/admin/controllers/users.js"></script> <script src="/admin/controllers/roles.js"></script> <script src="/admin/controllers/profile.js"></script> <script src="/admin/controllers/settings.js"></script> <script src="/admin/controllers/packages.js"></script> <script src="/admin/controllers/common.js"></script> <script src="/admin/services.js"></script> <script src="/scripts/bootstrap.js"></script> <script src="/scripts/moment.js"></script> </body> </html>
ps:这个东西没什么难度,逻辑都在仓库中,要源码的同学我后续分离出来了再贴出来。当然这个又很多方式,我也不是要秀什么框架,但我目前项目的需求是要这么分开的。一个控制器是可用解决所有问题,但我其他模型也要分页又要便于测试难道我都写在控制器中吗?
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
ASP.NET MVC 4使用PagedList.Mvc分页的实现代码
-
ASP.NET MVC5 实现分页查询的示例代码
-
Asp.Net MVC 分页、检索、排序整体实现代码
-
ASP.Net MVC+Data Table实现分页+排序功能的方法
-
ASP.NET MVC 4使用PagedList.Mvc分页的实现代码
-
Asp.Net MVC实现分页、检索、排序的代码展示
-
Asp.Net MVC 分页、检索、排序整体实现代码
-
关于ASP.NET MVC4如何使用PagedList.Mvc实现分页功能的示例代码
-
Asp.Net MVC实现分页、检索、排序的代码展示
-
关于ASP.NET MVC4如何使用PagedList.Mvc实现分页功能的示例代码