如何使搜索/分页在我的应用程序中可重用

how do I make searching/pagination reusable in my application?

本文关键字:应用程序 我的 何使 搜索 分页      更新时间:2023-09-26

我们决定在应用程序中使用服务器端分页。实现非常简单:

  • 假设我们在服务器端有这样一个搜索操作:

    [HttpGet]
    public ActionResult GetPeopleByName(String term, Int32 itemsPerPage = 10, Int32 page = 0) {
        var matches = this.people.Where(i => i.Name.Contains(term));
        return Json(
            data: new {
                people = matches.Skip(itemsPerPage * page).Take(itemsPerPage).OrderBy(i => i.Name),
                total = matches.Count()
            },
            behavior: JsonRequestBehavior.AllowGet
        );
    }
    
  • 在客户端上,我们有一个subscriptionHolderController:

    app.controller('subscriptionHolderController', ['$scope', '$http', function($scope, $http) {
        $scope.matches = [];
        $scope.itemsPerPage = 5;
        $scope.currentPage = 0;
        $scope.searchTerm = '';
        // $scope.prevPage = function() { ... };
        // $scope.prevPageDisabled = function() { ... };
        // $scope.nextPage = function() { ... };
        // $scope.nextPageDisabled = function() { ... };
        $scope.pageCount = function() { 
            return Math.ceil($scope.totalPages / $scope.itemsPerPage); 
        };
        $scope.$watch('currentPage', function() { $scope.search(); });
        $scope.search = function() {
            if($scope.searchTerm === '') return;
            // initiate a GET-request with params: { page: $scope.currentPage, term: $scope.searchTerm, itemsPerPage: $scope.itemsPerPage }
        }
        $scope.matchesFound = function () { return $scope.matches.length > 0; }
    }]);
    

问题

结果我们得到了一个简单的CCD_ 2。但我们的应用程序需要另外一种具有一些附加功能的搜索类型,它不使用搜索词,其结果集应该以与上面所示相同的方式进行分页

如何为不同类型的搜索重用分页逻辑?

在服务器端,您可以返回一个泛型类,该类将保存您的数据,即行的总数。

public class PagedResult<T>
    {
        public PagedResult(IEnumerable<T> data, long total)
        {
            Data = data;
            Total = total;
        }
        public PagedResult()
        {
        }
        public IEnumerable<T> Data { get; set; }
        public long Total { get; set; }
    }

你也可以为任何函数抽象输入参数,比如:

public class PageInfo
    {
        public int Page { get; set; }           
        public int PageSize { get; set; }
        public int Skip
        {
            get
            {
                return PageSize*(Page - 1);
            }
        }
        public PageInfo(int page, int pageSize)
        {
            Page = page;
            PageSize = pageSize;
        }
    }

一个实际的例子可能是:

[HttpGet]
public ActionResult GetPeopleByName(String term, PageInfo pageinfo) {
    var matches = this.people.Where(i => i.Name.Contains(term));
    var pagedResult =  new PagedResult<AnySearchType>{
            data = matches.Skip(pageinfo.skip).Take(pageinfo.size).OrderBy(i => i.Name),
            total = matches.Count()
        };
    return Json(
        data: pagedResult,
        behavior: JsonRequestBehavior.AllowGet
    );
}

在客户端,您可以使用一个指令,通过传递以下参数来抽象分页的逻辑:

视图

<div class="box-content" scroll-pager="pagerConfig">
your data
<div>

控制器:

你可以通过一些配置,比如:

$scope.pagerConfig = {
        pageSize: 10,
        data: 'model.Data', // some text reference to your model
        total: 'model.Total', // some text reference to your model
        currentPage: 1,
        searchParamName: 'Text',// some text reference to your model
        resource: projectResource,// pass a resource object
        success: function (data, page, total) {
            }
    };

为分页创建单独的类。在这个类中,您可以定义分页方法,您可以将分页应用于任何类型,并且使用不同的参数,您还可以自定义要应用分页的字段和页面大小。

这就是我最终得到的:

app.directive('pager', function() {
    return {
        restrict: 'EA',
        scope: {
            onChange: '&',
            items: '=',
            itemsPerPage: '@'
        },
        replace: true,
        templateUrl: '/scripts/js/app/pager.html',
        link: function(scope, el, attrs) {
            scope.currentPage = 1;
            scope.isFirstPage = function() {
                return scope.currentPage === 1;
            }
            scope.decPage = function() {
                if(scope.currentPage > 1) --scope.currentPage;
            }
            scope.isLastPage = function() {
                return scope.currentPage >= scope.totalPages();
            }
            scope.totalPages = function() {
                return Math.ceil(scope.items.total / scope.itemsPerPage);
            }
            scope.incPage = function() {
                if(!scope.isLastPage()) ++scope.currentPage;
            }
            scope.$watch("currentPage", function(value) {
                scope.onChange({page: value, itemsPerPage: scope.itemsPerPage});
            });
        }
    };
});

标记

<div id="content" ng-app="subscriptionsManager">
    <div ng-controller="subscriptionHolderController">
        <div class="row">
            <div class="columns medium-6 large-6">
                <div class="searchbar">
                    <div class="searchbar-inner">
                        <input ng-model="searchTerm" type="text" />
                        <button ng-click="search(1, 35)" class="tiny">search</button>
                    </div>
                </div>
            <div pager items-per-page="35" items="data" on-change="respondToTheChange(page, itemsPerPage)"></div>
           </div>
           <div class="columns medium-6 large-6">
                <div class="button-group filter-sample">
                     <button ng-click="toggleState1()" ng-class="{true: 'selected', false: 'secondary'}[state1]" class="tiny">filter1</button>
                     <button ng-click="toggleState2()" ng-class="{true: 'selected', false: 'secondary'}[state2]" class="tiny">filter2</button>
                     <button ng-click="toggleState3()" ng-class="{true: 'selected', false: 'secondary'}[state3]" class="tiny">filter3</button>
                     <button ng-click="search2(1, 35)" class="tiny">search</button>
                </div>
                <div pager items-per-page="35" items="data2" on-change="respondToTheChange2(page, itemsPerPage)"></div>
             </div>
        </div>
    </div>
</div>    

控制器

// search using a search term
$scope.data = { items: [], total: 0 };
$scope.searchTerm = '';
$scope.state1 = false;
$scope.state2 = false;
$scope.state3 = false;
$scope.toggleState1 = function() {
    $scope.state1 = !$scope.state1;
}
$scope.toggleState2 = function() {
    $scope.state2 = !$scope.state2;
}
$scope.toggleState3 = function() {
    $scope.state3 = !$scope.state3;
}
$scope.search = function(page, itemsPerPage) {
    if($scope.searchTerm === '') return;
    if(!angular.isDefined(page) || page == null) page = 1;
    if(!angular.isDefined(itemsPerPage) || itemsPerPage == null) itemsPerPage = 35;
    $http({
        url: '/subscriptions/GetSubscribersByNamePaged',
        method: 'GET',
        params: { term: $scope.searchTerm, page: page, itemsPerPage: itemsPerPage }
    })
    .success(function(data, status, headers, config) {
        $scope.data = data;
    }).error(function(data, status, headers, config) {
        console.log('error: ' + data);
    });
}
// search using anything else
$scope.search2 = function(page, itemsPerPage) {
    if(!angular.isDefined(page) || page == null) page = 1;
    if(!angular.isDefined(itemsPerPage) || itemsPerPage == null) itemsPerPage = 35;
    $http({
        url: '/subscriptions/GetSubscribersByFilters',
        method: 'GET',
        params: { state1: $scope.state1, state2: $scope.state2, state3: $scope.state3, page: page, itemsPerPage: itemsPerPage }
    })
    .success(function(data, status, headers, config) {
        $scope.data2 = data;
    }).error(function(data, status, headers, config) {
        console.log('error: ' + data);
    });
}
// bind searches!
$scope.respondToTheChange = function(page, itemsPerPage) {
    $scope.search(page, itemsPerPage);
}
$scope.respondToTheChange2 = function(page, itemsPerPage) {
    $scope.search2(page, itemsPerPage);
}