zoukankan      html  css  js  c++  java
  • 前端使用AngularJS的$resource,后端ASP.NET Web API,实现分页、过滤

    在上一篇中实现了增删改查,本篇实现分页和过滤。

    本系列包括:

    1、前端使用AngularJS的$resource,后端ASP.NET Web API,实现增删改查
    2、前端使用AngularJS的$resource,后端ASP.NET Web API,实现分页、过滤


    后端添加分页、排序逻辑



    首先要在后端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<StudentVm>(orderBy))
                {
                    var orderByExpression = QueryHelper.GetPropertyExpression<StudentVm>(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<T>(string propertyName)
            {
                return typeof(T).GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance) != null;
            }
    
            public static Expression<Func<T, string>> GetPropertyExpression<T>(string propertyName)
            {
                if (typeof(T).GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance) == null)
                {
                    return null;
                }
    
                var paramterExpression = Expression.Parameter(typeof(T));
    
                return (Expression<Func<T, string>>)Expression.Lambda(Expression.PropertyOrField(paramterExpression, propertyName), paramterExpression);
            }
    
            public static Expression<Func<T, int>> GetPropertyExpressionInt<T>(string propertyName)
            {
                if (typeof(T).GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance) == null)
                {
                    return null;
                }
    
                var paramterExpression = Expression.Parameter(typeof(T));
    
                return (Expression<Func<T, int>>)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 bootstrap
    npm 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。

    <!DOCTYPE html>
    <html lang="en" ng-app="studentManagement">
    <head>
      <meta charset="UTF-8">
      <title>{{title}}</title>
      <link rel="stylesheet" href="node_modules/alertify/themes/alertify.core.css"/>
      <link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css"/>
      <link rel="stylesheet" href="vendor/dirPagination/dirPagination.css"/>
    </head>
    <body>
      <div>
        <p>
          <a href="#/">Students</a>
          &nbsp;&nbsp;
          <a href="#/Create">Create Student</a>
        </p>
      </div>
    
      <div class="container">
        <div class="row">
          <div class="col-lg-8">
            <div ng-view></div>
          </div>
        </div>
      </div>
    
    
    
      <script src="node_modules/angular/angular.min.js"></script>
      <script src="node_modules/angular-route/angular-route.min.js"></script>
      <script src="node_modules/angular-resource/angular-resource.min.js"></script>
      <script src="node_modules/angular-cookies/angular-cookies.min.js"></script>
      <script src="node_modules/alertify/lib/alertify.min.js"></script>
      <script src="node_modules/jquery/dist/jquery.min.js"></script>
      <script src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
    
      <script src="app.js"></script>
    
      <script src="service/studentService.js"></script>
      <script src="controller/studentUpdateCtrl.js"></script>
      <script src="controller/studentsCtrl.js"></script>
      <script src="controller/studentCreateCtrl.js"></script>
      <script src="vendor/dirPagination/dirPagination.js"></script>
    
    
    </body>
    </html>

    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变量值赋值给它。

    <!--显示内容的控制器-->
    <div class="my-controller">
    
    
      <div class="row">
        <div class="col-xs-4">
          <h3>当前页: {{ currentPage }}</h3>
        </div>
        <div class="col-xs-4">
          <label for="search">搜索:</label>
          <input ng-model="q" id="search" class="form-control" placeholder="Filter text">
        </div>
        <div class="col-xs-4">
          <label for="search">每页条数:</label>
          <input type="number" min="1" max="100" class="form-control" ng-model="pageSize">
        </div>
      </div>
      <br>
      <div class="panel panel-default">
        <div class="panel-body">
    
          <table>
            <thead>
              <tr>
                <th>Name</th><th>Age</th><th>Actions</th>
              </tr>
            </thead>
            <tbody>
             <!--itemsPerPage是必须的,要放在所有过滤的最后面,表示页容量-->
             <!--dir-paginate的pagination-id属性,可选,和dir-pagination-controls中的pagination-id对应-->
             <!--dir-paginate的current-page属性,可选,默认会读取$scope中的_currentPage字段-->
             <!--dir-paginate的total-items属性,可选,用来读取服务端数据,先把总页数显示出来-->
              <tr dir-paginate="student in students | filter:q | itemsPerPage: pageSize" total-items="total" current-page="currentPage">
                <td>{{student.name}}</td>
                <td>{{student.age}}</td>
                <td>
                  <a href="#/Student/{{student.Id}}">更新</a>
                  &nbsp;&nbsp;
                  <a href="javascript:void(0)" ng-click="$parent.removeStudent(student.Id, student)">移除</a>
                </td>
              </tr>
            </tbody>
          </table>
    
        </div>
      </div>
    </div>
    
    <!--显示分页的控制器-->
    <!--<div ng-controller="OtherController" class="other-controller">-->
    <div class="other-controller">
      <div class="text-center">
        <!--其它属性包括:max-size, direction-links,boundary-links,pagination-id,auto-hide-->
        <!--max-size:可选,默认为最大是9,最小是5-->
        <!--direction-links:可选,默认true,决定是否显示向前向后按钮-->
        <!--boundary-links,可选,默认false,决定是否显示首页和最后一页按钮-->
        <!--on-page-change,可选,默认为null,声明当点击页数按钮后的回调函数,一旦声明,必须传入newPageNumber形参,必须在$scope中声明回调函数,比如这里的pageCHnageHandler函数-->
        <!--pagination-id,可选,与dir-paginate中的pagination-id值对应-->
        <!--template-url,可选,默认值是directives/pagination/dirPagination.tpl.html,也可以在全局config中,通过paginationTemplateProvider配置-->
        <!--auto-hide,可选,默认是true,决定当没有足够的分页数据时,分页是否显示-->
        <!--这里的dir-pagination-controls不能离开dir-paginate而单独存在,否则报错-->
        <dir-pagination-controls boundary-links="true" on-page-change="pageChangeHandler(newPageNumber)" template-url="vendor/dirPagination/dirPagination.tpl.html"></dir-pagination-controls>
      </div>
    </div>

    本系列结束☺


    参考资料:

    有关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

  • 相关阅读:
    Linux命令之 文件归档管理
    C#总结项目《影院售票系统》编写总结完结篇
    C#总结项目《影院售票系统》编写总结三
    C#中MD5加密
    C#中的序列化与反序列化
    C#总结项目《影院售票系统》编写总结二
    C#总结项目《影院售票系统》编写总结一
    java多线程与线程并发四:线程范围内的共享数据
    java多线程与线程并发三:线程同步通信
    java多线程与线程并发二:线程互斥
  • 原文地址:https://www.cnblogs.com/darrenji/p/5016751.html
Copyright © 2011-2022 走看看