在上一篇中实现了增删改查,本篇实现分页和过滤。
本系列包括:1、2、后端添加分页、排序逻辑
首先要在后端API中添加分页的逻辑。对于分页来说,一般需要从前端获取页容量和当前页变量,还可以获取有关排序的变量。大致这样:
public IHttpActionResult Get(int pageSize, int pageNumber, string orderBy = ""){}
在StudentsController这个控制器中,增加一个Get重载方法,用于接受分页变量。
[RoutePrefix("api/Students")] public class StudentsController : ApiController { private StudentsReop _reop = new StudentsReop(); //GET api/Students public HttpResponseMessage Get() { var students = _reop.Query().ToList(); return Request.CreateResponse(HttpStatusCode.OK, students); } //GET api/Students/5 public HttpResponseMessage Get(int id) { var student = _reop.Get(id); return Request.CreateResponse(HttpStatusCode.OK, student); } //GET api/Students/pageSize/pageNumber/orderBy(optional) [Route("{pageSize:int}/{pageNumber:int}/{orderBy:alpha?}")] public IHttpActionResult Get(int pageSize, int pageNumber, string orderBy = "") { var totalCount = _reop.Query().ToList().Count();//总数量 var totalPages = Math.Ceiling((double)totalCount/pageSize);//总页数和pageSize有关 var tempResult = _reop.Query(); if (QueryHelper.PropertyExists(orderBy)) { var orderByExpression = QueryHelper.GetPropertyExpression (orderBy); tempResult = tempResult.AsQueryable().OrderBy(orderByExpression); } else { tempResult = tempResult.OrderBy(c => c.Id); } var students = tempResult.Skip((pageNumber - 1) * pageSize) .Take(pageSize) .ToList(); var result = new { TotalCount = totalCount, TotalPages = totalPages, Students = students }; return Ok(result); } //POST api/Students public void Post([FromBody]StudentVm student) { _reop.Post(student); } //PUT api/Students/5 public void Put(int id, [FromBody]StudentVm student) { _reop.Put(id, student); } //DELETE api/Students public void Delete(int id) { _reop.Delete(id); } }
以上,Get(int pageSize, int pageNumber, string orderBy = "")方法中,首先获取排序后的临时数据,再使用skip,take方法获取分页数据,最后返回给前端一个json数据,其中,TotalCount表示总数据量,Students表示当前页下的数据,这2个字段供前端读取。另外,QueryHelper封装了可以根据模型字段获取表达式树的方法。
internal static class QueryHelper { public static bool PropertyExists(string propertyName) { return typeof(T).GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance) != null; } public static Expression > GetPropertyExpression (string propertyName) { if (typeof(T).GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance) == null) { return null; } var paramterExpression = Expression.Parameter(typeof(T)); return (Expression >)Expression.Lambda(Expression.PropertyOrField(paramterExpression, propertyName), paramterExpression); } public static Expression > GetPropertyExpressionInt (string propertyName) { if (typeof(T).GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance) == null) { return null; } var paramterExpression = Expression.Parameter(typeof(T)); return (Expression >)Expression.Lambda(Expression.PropertyOrField(paramterExpression, propertyName), paramterExpression); } }
此时,通过浏览器可以获取到分页数据。比如:http://localhost:49621/api/Students/5/2/Name
前端准备
前端需要用到Michael Bromley写的一个分页Directive。 通过如下可安装:bower install angular-utils-pagination或npm install angular-utils-pagination还需要安装bootstrap和jquery:npm install bootstrapnpm install jquery文件结构变为:app.js 主module,路由都在这里配置index.html 主视图,引用所有的css,js文件,提供让其它部分视图呈现的一块区域<div ng-view></div>.....service/ 自定义服务,$resouce的核心就封装在这里..........studentService.js.....controller/..........studentsCtrl.js 列表..........studentUpdateCtrl.js 更新..........studentCreateCtrl.js 添加.....views/..........Students.html 列表..........StudentInfo.html 更新..........StudentCreate.html 添加vendor/......diaPagination/ Michael Bromley写的directive..........dirPagination.css..........dirPagination.js..........dirPagination.tpl.html
index.html
相对于上一篇,添加了如下:<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css"/><link rel="stylesheet" href="vendor/dirPagination/dirPagination.css"/><script src="node_modules/jquery/dist/jquery.min.js"></script><script src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script><script src="vendor/dirPagination/dirPagination.js"></script>并使用上了bootstrap。
{ {title}}
app.js
对比上一篇,这里添加了对angularUtils.directives.dirPagination这个module的依赖,去掉了/这个路由中有关resolve获取数据的机制,因为现在要分页了,每次都要获取数据,而不是把所有数据先从服务端获取到放到路由中。
"use strict";var studentsManagement = angular.module("studentManagement",["ngResource","ngCookies","ngRoute","angularUtils.directives.dirPagination"]) .run(function($rootScope){ $rootScope.title = "Home"; }) .config(["$routeProvider","$locationProvider", function($routeProvider, $locationProvider){ //在全局配置分页模板 需要注入paginationTemplateProvider //paginationTemplateProvider.setPath('path/to/dirPagination.tpl.html'); //关于url的基本配置 //$locationProvider.html5Mode({ // enabled: true, // requireBase: false //}); //配置路由 $routeProvider.when("/", { templateUrl: "views/Students.html", controller: "studentsCtrl" //resolve: { // students: function($q,studentDataService){ // // var queryArgs = { // pageSize: 3, // pageNumber: 1, // orderBy: "id" // }; // // //$q异步执行方法 // var deferred = $q.defer(); // studentDataService.query(function(data){ // deferred.resolve(data); // }); // // return deferred.promise; // } //} }).when("/Student/:id",{ templateUrl: "views/StudentInfo.html", controller: "studentUpdateCtrl", resolve: { student: function($q, studentDataService, $route){ var defered = $q.defer(); //从路由中获取id的值 var id = $route.current.params.id; studentDataService.get({id: id}, function(data){ defered.resolve(data); }); return defered.promise; } } }).when("/Create",{ templateUrl: "views/CreateStudent.html", controller: "studentCreateCtrl" }); }]);
以上,我们还可以通过paginationTemplateProvider.setPath('path/to/dirPagination.tpl.html');在全局中配置分页模板,但这里没有。
studentService.js
用$resouce封装请求数据这块有变化,因为要带上分页、排序参数。
angular.module('studentManagement').factory("studentDataService",["$resource", function($resource){ var baseUrl = "http://localhost:49621/api/Students"; return $resource("http://localhost:49621/api/Students",{},{ query: { method: "GET", url: baseUrl + "/:pageSize/:pageNumber/:orderBy", params: {pageSize: '@pageSize', pageNumber: '@pageNumber', orderBy: '@orderBy'} }, //query: { // method: "GET", // isArray: true, //}, create: {method: "POST"}, get: {method: "GET", url: baseUrl + "?id=:id"}, remove: {method: "DELETE", url: baseUrl + "?id=:id"}, update: {method: "PUT", url: baseUrl + "?id=:id"} })}]);
studentsControl.js
angular.module('studentManagement').controller("studentsCtrl", ['$scope', '$route', '$rootScope', 'studentDataService', function ($scope, $route, $rootScope, studentDataService) { $scope.students = []; $scope.total = 0; //总数据条数 $scope.currentPage = 1; $scope.pageSize = 3; //页容量 //初次加载 getPageStudents(1); $scope.pageChangeHandler = function (num) { getPageStudents(num); }; //获取分页数据 function getPageStudents(pageNumber){ var queryArgs = { pageSize: $scope.pageSize, pageNumber: pageNumber, orderBy: 'name' }; studentDataService.query(queryArgs).$promise.then(function(result){ $scope.students = result.students; $scope.total = result.totalCount; }, function(result){ //如果失败 console.log('fail'); }); } $rootScope.title = "Students"; //$scope.students = $route.current.locals.students;//students在路由resolve中定义 //删除 $scope.removeStudent = function (id, student) { studentDataService.remove({id: id}).$promise.then(function () { //获取student在当前集合中的索引 var index = $scope.students.indexOf(student); $scope.students.splice(index, 1); alertify.log(student.Name + ' is removed'); }); };}]); //.controller("OtherController", ["$scope", function ($scope) { // $scope.pageChangeHandler = function (num) { // console.log('going to page ' + num); // }; //}]);
以上,初次加载以及点击分页按钮的时候都触发getPageStudents方法,传入页容量和当前页变量。
Students.html
在dir-paginate中多了一个total-items属性,把从controller中获取到的total变量值赋值给它。
本系列结束☺
参考资料:有关Web API配合AngularJS分页:● https://code.msdn.microsoft.com/AngularJS-with-Web-API-43e5de16有关AngularJS分页的Directive:● http://www.michaelbromley.co.uk/blog/108/paginate-almost-anything-in-angularjs● https://github.com/michaelbromley/angularUtils/tree/master/src/directives/pagination#working-with-asynchronous-data