Merge pull request #3321 from ssilvert/pagination

KEYCLOAK-3507: Pagination for clients and roles in admin console
This commit is contained in:
Stan Silvert 2016-10-17 07:26:04 -04:00 committed by GitHub
commit a9ce6b9f81
6 changed files with 200 additions and 71 deletions

View file

@ -2802,3 +2802,81 @@ module.directive('kcOnReadFile', function ($parse) {
}
};
});
module.controller('PagingCtrl', function ($scope) {
$scope.currentPageInput = 1;
$scope.firstPage = function() {
if (!$scope.hasPrevious()) return;
$scope.currentPage = 1;
$scope.currentPageInput = 1;
};
$scope.lastPage = function() {
if (!$scope.hasNext()) return;
$scope.currentPage = $scope.numberOfPages;
$scope.currentPageInput = $scope.numberOfPages;
};
$scope.previousPage = function() {
if (!$scope.hasPrevious()) return;
$scope.currentPage--;
$scope.currentPageInput = $scope.currentPage;
};
$scope.nextPage = function() {
if (!$scope.hasNext()) return;
$scope.currentPage++;
$scope.currentPageInput = $scope.currentPage;
};
$scope.hasNext = function() {
return $scope.currentPage < $scope.numberOfPages;
};
$scope.hasPrevious = function() {
return $scope.currentPage > 1;
};
});
module.directive('kcPaging', function () {
return {
scope: {
currentPage: '=',
currentPageInput: '=',
numberOfPages: '='
},
restrict: 'E',
replace: true,
controller: 'PagingCtrl',
templateUrl: resourceUrl + '/templates/kc-paging.html'
}
});
// Tests the page number input from currentPageInput to see
// if it represents a valid page. If so, the current page is changed.
module.directive('kcValidPage', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
ctrl.$validators.inRange = function(modelValue, viewValue) {
if (viewValue >= 1 && viewValue <= scope.numberOfPages) {
scope.currentPage = viewValue;
}
return true;
}
}
}
});
// filter used for paged tables
module.filter('startFrom', function () {
return function (input, start) {
if (input) {
start = +start;
return input.slice(start);
}
return [];
};
});

View file

@ -731,9 +731,21 @@ module.controller('ClientImportCtrl', function($scope, $location, $upload, realm
});
module.controller('ClientListCtrl', function($scope, realm, clients, Client, serverInfo, $route, Dialog, Notifications) {
module.controller('ClientListCtrl', function($scope, realm, clients, Client, serverInfo, $route, Dialog, Notifications, filterFilter) {
$scope.realm = realm;
$scope.clients = clients;
$scope.currentPage = 1;
$scope.currentPageInput = 1;
$scope.pageSize = 20;
$scope.numberOfPages = Math.ceil($scope.clients.length/$scope.pageSize);
$scope.$watch('search', function (newVal, oldVal) {
$scope.filtered = filterFilter($scope.clients, newVal);
$scope.totalItems = $scope.filtered.length;
$scope.numberOfPages = Math.ceil($scope.totalItems/$scope.pageSize);
$scope.currentPage = 1;
$scope.currentPageInput = 1;
}, true);
$scope.removeClient = function(client) {
Dialog.confirmDelete(client.clientId, 'client', function() {

View file

@ -1291,9 +1291,21 @@ module.controller('RealmRevocationCtrl', function($scope, Realm, RealmPushRevoca
});
module.controller('RoleListCtrl', function($scope, $route, Dialog, Notifications, realm, roles, RoleById) {
module.controller('RoleListCtrl', function($scope, $route, Dialog, Notifications, realm, roles, RoleById, filterFilter) {
$scope.realm = realm;
$scope.roles = roles;
$scope.currentPage = 1;
$scope.currentPageInput = 1;
$scope.pageSize = 20;
$scope.numberOfPages = Math.ceil($scope.roles.length/$scope.pageSize);
$scope.$watch('searchQuery', function (newVal, oldVal) {
$scope.filtered = filterFilter($scope.roles, {name: newVal});
$scope.totalItems = $scope.filtered.length;
$scope.numberOfPages = Math.ceil($scope.totalItems/$scope.pageSize);
$scope.currentPage = 1;
$scope.currentPageInput = 1;
}, true);
$scope.removeRole = function (role) {
Dialog.confirmDelete(role.name, 'role', function () {

View file

@ -4,14 +4,14 @@
<kc-tooltip>{{:: 'clients.tooltip' | translate}}</kc-tooltip>
</h1>
<table class="table table-striped table-bordered">
<table class="datatable table table-striped table-bordered dataTable no-footer">
<thead>
<tr>
<th class="kc-table-actions" colspan="6">
<div class="form-inline">
<div class="form-group">
<div class="input-group">
<input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search.clientId" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
<input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search.clientId" class="form-control search" onkeyup="if(event.keyCode === 13){$(this).next('I').click();}">
<div class="input-group-addon">
<i class="fa fa-search" type="submit"></i>
</div>
@ -33,7 +33,7 @@
</tr>
</thead>
<tbody>
<tr ng-repeat="client in clients | filter:search | orderBy:'clientId'">
<tr ng-repeat="client in clients| filter:search | orderBy:'clientId' | startFrom:(currentPage - 1) * pageSize | limitTo:pageSize">
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></td>
<td translate="{{client.enabled}}"></td>
<td ng-class="{'text-muted': !client.baseUrl}">
@ -45,11 +45,12 @@
<td class="kc-action-cell" data-ng-show="access.manageClients" data-ng-click="removeClient(client)">{{:: 'delete' | translate}}</td>
</tr>
<tr data-ng-show="(clients | filter:search).length == 0">
<td class="text-muted" colspan="3" data-ng-show="search.clientId">{{:: 'no-results' | translate}}</td>
<td class="text-muted" colspan="3" data-ng-hide="search.clientId">{{:: 'no-clients-available' | translate}}</td>
<td class="text-muted" colspan="4" data-ng-show="search.clientId">{{:: 'no-results' | translate}}</td>
<td class="text-muted" colspan="4" data-ng-hide="search.clientId">{{:: 'no-clients-available' | translate}}</td>
</tr>
</tbody>
</table>
<kc-paging current-page='currentPage' number-of-pages='numberOfPages' current-page-input='currentPageInput'></kc-paging>
</div>
<kc-menu></kc-menu>

View file

@ -6,14 +6,14 @@
<li><a href="#/realms/{{realm.realm}}/default-roles">{{:: 'default-roles' | translate}}</a></li>
</ul>
<table class="table table-striped table-bordered">
<table class="datatable table table-striped table-bordered dataTable no-footer">
<thead>
<tr>
<th class="kc-table-actions" colspan="5">
<div class="form-inline">
<div class="form-group">
<div class="input-group">
<input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="searchQuery" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
<input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="searchQuery" class="form-control search" onkeyup="if (event.keyCode === 13){$(this).next('I').click(); }">
<div class="input-group-addon">
<i class="fa fa-search" type="submit"></i>
</div>
@ -34,7 +34,7 @@
</tr>
</thead>
<tbody>
<tr ng-repeat="role in roles | orderBy:'name' | filter:{name: searchQuery}">
<tr ng-repeat="role in roles| filter:{name: searchQuery} | orderBy:'name'| startFrom:(currentPage - 1) * pageSize | limitTo:pageSize">
<td><a href="#/realms/{{realm.realm}}/roles/{{role.id}}">{{role.name}}</a></td>
<td translate="{{role.composite}}"></td>
<td>{{role.description}}</td>
@ -42,11 +42,12 @@
<td class="kc-action-cell" data-ng-click="removeRole(role)">{{:: 'delete' | translate}}</td>
</tr>
<tr data-ng-show="(roles | filter:{name: searchQuery}).length == 0">
<td class="text-muted" colspan="3" data-ng-show="searchQuery">{{:: 'no-results' | translate}}</td>
<td class="text-muted" colspan="3" data-ng-hide="searchQuery">{{:: 'no-realm-roles-available' | translate}}</td>
<td class="text-muted" colspan="4" data-ng-show="searchQuery">{{:: 'no-results' | translate}}</td>
<td class="text-muted" colspan="4" data-ng-hide="searchQuery">{{:: 'no-realm-roles-available' | translate}}</td>
</tr>
</tbody>
</table>
<kc-paging current-page='currentPage' number-of-pages='numberOfPages' current-page-input='currentPageInput'></kc-paging>
</div>
<kc-menu></kc-menu>

View file

@ -0,0 +1,25 @@
<div ng-hide="numberOfPages < 2" class="dataTables_footer">
<div class="dataTables_paginate paging_bootstrap_input">
<ul class="pagination">
<li class="first" ng-class="{disabled: !hasPrevious()}" ng-click="firstPage()">
<span class="i fa fa-angle-double-left"></span>
</li>
<li class="prev" ng-class="{disabled: !hasPrevious()}" ng-click="previousPage()">
<span class="i fa fa-angle-left"></span>
</li>
</ul>
<div class="pagination-input">
<input ng-model="currentPageInput" kc-valid-page class="paginate_input" type="text">
<span class="paginate_of">of <b>{{numberOfPages}}</b></span>
</div>
<ul class="pagination">
<li class="next" ng-class="{disabled: !hasNext()}" ng-click="nextPage()">
<span class="i fa fa-angle-right"></span>
</li>
<li class="last" ng-class="{disabled: !hasNext()}" ng-click="lastPage()">
<span class="i fa fa-angle-double-right">
</span>
</li>
</ul>
</div>
</div>