how to make a correct search when pagination - javascript

I can not understand. How to make a correct search when pagination?
English for bad writing.
I did so:
var app = angular.module('appTelDirectory', []);
app.controller('directoryList', function($scope) {
$scope.currentPage = 0;
$scope.pageSize = 10;
$scope.users = [{}]
$scope.numberOfPages = function() {
return Math.ceil($scope.users.length / $scope.pageSize);
}
for (var i = 0; i < 45; i++) {
$scope.users.push({
'name': 'user' + i
});
}
});
app.filter('startFrom', function() {
return function(input, start) {
start = +start; //parse to int
return input.slice(start);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="appTelDirectory" ng-controller="directoryList">
<input placeholder="Поиск..." ng-model="searchAll" class="form-control">
<ul>
<li ng-repeat="item in users | filter:searchAll | startFrom:currentPage*pageSize | limitTo:pageSize">{{item.name}}</li>
</ul>
<table>
<tr ng-repeat="item in users | startFrom:currentPage*pageSize | limitTo:pageSize">
</table>
<button ng-disabled="currentPage == 0" ng-click="currentPage=currentPage-1">Previous</button>
{{currentPage+1}}/{{numberOfPages()}}
<button ng-disabled="currentPage >= users.length/pageSize - 1" ng-click="currentPage=currentPage+1">
Next
</button>
</div>
How do I change the number of items, depending on the user list. NumberOfPages unchanged...

You can use a separate list for it like this. Basically, I'm using another list filteredUsers. Now instead of using filter in the view i.e. filter:searchAll, I'm doing the same thing using the underlying $filter service in the $watch which will be invoked as I type in the field.
Now, we always have the filtered users in the filteredUsers scope variable so your further calculation now can be based on the $scope.filteredUsers not on $scope.users.
var app = angular.module('appTelDirectory', []);
app.controller('directoryList', function($scope, $filter) {
$scope.currentPage = 0;
$scope.pageSize = 10;
$scope.users = [{}];
// Using a separate list of filtered users
$scope.filteredUsers = [{}];
$scope.numberOfPages = function() {
return Math.ceil($scope.filteredUsers.length / $scope.pageSize);
}
for (var i = 0; i < 45; i++) {
$scope.users.push({
'name': 'user' + i
});
}
$scope.filteredUsers = angular.copy($scope.users);
$scope.$watch('searchAll', function(newValue) {
// Manually filtering here instead doing in the view
$scope.filteredUsers = $filter('filter')($scope.users, {$: newValue});
});
});
app.filter('startFrom', function() {
return function(input, start) {
start = +start; //parse to int
return input.slice(start);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="appTelDirectory" ng-controller="directoryList">
<input placeholder="Поиск..." ng-model="searchAll" class="form-control">
<ul>
<li ng-repeat="item in filteredUsers | startFrom:currentPage*pageSize | limitTo:pageSize">{{item.name}}</li>
</ul>
<table>
<tr ng-repeat="item in users | startFrom:currentPage*pageSize | limitTo:pageSize">
</table>
<button ng-disabled="currentPage == 0" ng-click="currentPage=currentPage-1">Previous</button>
{{currentPage+1}}/{{numberOfPages()}}
<button ng-disabled="currentPage >= filteredUsers.length/pageSize - 1" ng-click="currentPage=currentPage+1">
Next
</button>
</div>

Related

SortBy not Sorting correctly?

I have a table with this ng-repeat
ticket in filteredTickets = (vm.tickets | filter : vm.search | orderBy : vm.propertyName : vm.reverse | limitTo:vm.itemsPerPage:vm.itemsPerPage*(vm.currentPage-1))
My thead call a function on ng-click
ng-click="vm.sortBy('title');"
And in my controller
// table ordering
vm.propertyName = 'ticketID'; // default order
vm.reverse = true;
vm.sortBy = function(propertyName) {
vm.reverse = (vm.propertyName === propertyName) ? !vm.reverse : false;
vm.propertyName = propertyName;
}
but it's not ordering well. I have multiple columns, some with integers other with strings and doesnt order correctly (it orders but randomly)
Any help?
You have limiTo filter, so when you change value of vm.propertyName you can see quite another set of tickets(not current, but with new sorting), may be exactly that creates for you a sensation of "random" sorting? At this case, you can set vm.currentPage = 1 inside vm.sortBy method to start new overview from first page or place limitTo filter before orderBy to fix current page and only then apply sorting to items in it, as shown below:
angular.module('app', []).controller('ctrl', function($scope){
var vm = this;
vm.propertyName = 'ticketID';
vm.itemsPerPage = 5;
vm.currentPage = 1;
vm.tickets = [];
for(var i = 0; i < 100; i++)
vm.tickets.push({
ticketID: i,
name: 'Name' + (100 - i)
})
vm.sortBy = function(propertyName) {
vm.reverse = (vm.propertyName === propertyName) ? !vm.reverse : false;
vm.propertyName = propertyName;
//or simply vm.currentPage = 1; without rest of stuff
}
vm.pages = function(value){
return Math.ceil(value);
}
})
<script src="//code.angularjs.org/snapshot/angular.min.js"></script>
<div ng-app='app' ng-controller='ctrl as vm'>
search by: <input type='text' ng-model='vm.search'/>
<br>
<button ng-click='vm.sortBy("ticketID")'>Sort by ticketID</button>
<button ng-click='vm.sortBy("name")'>Sort by name</button>
<ul>
<li ng-repeat='ticket in filteredTickets = (vm.tickets | filter : vm.search | limitTo : vm.itemsPerPage : vm.itemsPerPage * (vm.currentPage - 1)) | orderBy : vm.propertyName : vm.reverse'>
{{ticket}}
</li>
</ul>
<button ng-disabled='vm.currentPage == 1' ng-click='vm.currentPage = vm.currentPage - 1'>Prev</button>
<button disabled>{{vm.currentPage}}</button>
<button ng-disabled='vm.currentPage == vm.pages((vm.tickets | filter : vm.search).length / vm.itemsPerPage)' ng-click='vm.currentPage = vm.currentPage + 1'>Next</button>
</div>

pass array from database into controller AngularJs

I have a little problem with this app that i'm building to learn AngularJs.
It is a Football Stats app. I choose Home Team and Away team from 2 dropdown menus.
Then i have to do some math operations and show only the results.
This is my html code:
<div class='dropdown'>
<span>Seleziona Nazione: </span>
<select class='opzioni' ng-model="nazioniSelected">
<option ng-repeat="nazione in nazioni track by $index" value="{{nazione}}">{{nazione}}
</option>
</select>
</div>
<div class='dropdown2'>
<span>Seleziona Campionato: </span>
<select class='opzioni' ng-model="campionatoSelected">
<option ng-repeat="team in teams | filter: {Paese:nazioniSelected} track by $index" value="{{team.Campionato}}">{{team.Campionato}}
</option>
</select>
</div>
<div class='SquadraCasa'>
<span>Seleziona Squadra Casa: </span>
<select class='opzioni' ng-model="HomeTeamSelected" >
<option ng-repeat="team in teams | filter:
{Campionato:campionatoSelected, Paese:nazioniSelected} track by $index"
value='{{team.Nome}}'>
{{team.Nome}}
</option>
</select>
</div>
<div class='SquadraTrasferta'>
<span>Seleziona Squadra Trasferta: </span>
<select class='opzioni' ng-model="AwayTeamSelected">
<option ng-repeat="team in teams | filter:
{Campionato:campionatoSelected, Paese:nazioniSelected} track by $index"
value='{{team.Nome}}'>{{team.Nome}}
</option>
</select>
</div>
<div class='InfoCasa'>
<ul ng-repeat='team in teams | filter: {Nome: HomeTeamSelected} track by $index'>
<img ng-show='HomeTeamSelected' src="{{team.Stemma}} ">
<p class='nome' ng-show='HomeTeamSelected'> {{team.Nome}} </p>
</ul>
</div>
<div class='InfoTrasferta'>
<ul ng-repeat='team in teams | filter: {Nome: AwayTeamSelected} track by $index'>
<img ng-show='AwayTeamSelected' src="{{team.Stemma}} ">
<p class='nome2' ng-show='AwayTeamSelected'> {{team.Nome}} </p>
</ul>
</div>
<div class="Calcolo" ng-show='AwayTeamSelected'>
<p>
Doppia Chance {{doppia}}
</p><br><br>
<p>
1x2
</p><br><br>
<p>
Over 1,5
</p><br><br>
<p>
Over 2,5
</p>
<button class="calcola" ng-click='calcolarisultato(HomeTeamSelected,AwayTeamSelected)' > Calcola
</div>
My problem is: In this ng-click i want to pass not only the name, but all the team variable, because i need all the data about the teams that i selected.
For now my controller is like this and is not working:
FootballNumbers.controller('teamController', function($scope, $route, $routeParams, $http) {
$http.get('/api/teams').then(function(response) {
$scope.teams = response.data;
console.log(response.data);
});
var squadra = $scope.teams;
$scope.nazioni = ['Austria', 'Belgio', 'Bulgaria', 'Croazia', 'Danimarca', 'Finlandia',
'Francia', 'Germania', 'Grecia', 'Inghilterra', 'Italia', 'Norvegia', 'Olanda',
'Polonia', 'Portogallo', 'Rep. Ceca', 'Romania', 'Russia', 'Spagna', 'Turchia', 'Svezia',
'Svizzera', 'Ucraina'
];
$scope.calcolarisultato = function(squadra1, squadra2) {
for (i = 0; i < squadra.length; i++) {
for (j = 0; j < squadra.length; i++) {
if (squadra[i].Nome == squadra1) {
if (squadra[j].Nome == squadra2) {
var media1 = (squadra[i].Classifica + squadra[i].ClassificaCasa +
squadra[i].Forma) / 3;
var media2 = (squadra[j].Classifica + squadra[j].ClassificaTrasferta +
squadra[i].Forma) / 3;
if ((media1 + 3) <= media2) {
$scope.doppia = '1 X';
} else if ((media2 + 3) <= media1) {
$scope.doppia = 'X 2';
} else {
$scope.doppia = 'Niente';
}
}
}
}
}
}
});
It says to me that squadra is not defined.
This is my solution but, as i said, i would like to pass in the function all the HomeTeam and AwayTeam data, not only the name as i did in this code. Any help please?
The problem is because of async execution. The value of squadra is undefined outside the API call because of asynchronous execution.
FootballNumbers.controller('teamController', function($scope, $route, $routeParams, $http) {
$http.get('/api/teams').then(function(response) {
$scope.teams = response.data;
console.log(response.data);
var squadra = $scope.teams;
});
$scope.nazioni = ['Austria', 'Belgio', 'Bulgaria', 'Croazia', 'Danimarca', 'Finlandia',
'Francia', 'Germania', 'Grecia', 'Inghilterra', 'Italia', 'Norvegia', 'Olanda',
'Polonia', 'Portogallo', 'Rep. Ceca', 'Romania', 'Russia', 'Spagna', 'Turchia', 'Svezia',
'Svizzera', 'Ucraina'
];
$scope.calcolarisultato = function(squadra1, squadra2) {
for (i = 0; i < squadra.length; i++) {
for (j = 0; j < squadra.length; i++) {
if (squadra[i].Nome == squadra1) {
if (squadra[j].Nome == squadra2) {
var media1 = (squadra[i].Classifica + squadra[i].ClassificaCasa +
squadra[i].Forma) / 3;
var media2 = (squadra[j].Classifica + squadra[j].ClassificaTrasferta +
squadra[i].Forma) / 3;
if ((media1 + 3) <= media2) {
$scope.doppia = '1 X';
} else if ((media2 + 3) <= media1) {
$scope.doppia = 'X 2';
} else {
$scope.doppia = 'Niente';
}
}
}
}
}
}
});
You init the teams with an async function but you init the squadra variable before to get these values. Instead of using the squadra value inside your controller, simply use the $scope.teams and remove this line:
var squadra = $scope.teams;
Then wherever in your controller you use squadra, change it with $scope.teams
Also think to define the constants as separated AngularJS constants instead of listing them in the controller, and then in the controller inject that constant. This keeps the code cleaner and more reusable.

Populate table from JSON with search

I was able to purely implement a grid from a JSON object - AngularJS ng-repeat to populate grid from array. However, due to the nature of the added indices, being able to create a search bar with ng-model and filter:search does not work - it only can search for the first in each table row.
var test= angular.module("app", []);
test.controller("appControl", function($scope, $http) {
$http.get("http://www.w3schools.com/angular/customers.php")
.success(function (response) {
$scope.data = response.records;
}
);
$scope.getFiltered= function(obj, idx){
//Set a property on the item being repeated with its actual index
//return true only for every 1st item in 5 items
return !((obj._index = idx) % 5);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<body ng-app='app' ng-controller='appControl'>
<input type='text' ng-model='search.Country' />
<table>
<tr ng-repeat="work in data | filter:getFiltered | filter:search">
<td>{{work.Country}}</td>
<td>{{data[work._index+1].Country}}</td>
<td>{{data[work._index+2].Country}}</td>
<td>{{data[work._index+3].Country}}</td>
<td>{{data[work._index+4].Country}}</td>
</tr>
</table>
</body>
The length of data may or not cause the table to look like a perfect rectangle.
I'm working on making a function to split up the array and create the grid in JavaScript itself, but I'm still not sure how to filter it via search input.
Second try (with the mentioned function, but no filters at all yet...):
var test= angular.module("app", []);
function createGrid(arr, width) {
newArr = [];
reps = Math.ceil(arr.length/width) * width;
k = 0;
for (var i = 0; i < reps/width; i++) {
newArr[i] = [];
}
for (var i = 0; i < reps/width; i++) {
for (var j = 0; j < width; j++) {
(arr[k]) ? newArr[i][j] = arr[k] : newArr[i][j] = "";
//console.log(i, j, arr[k]);
k++;
}
}
return newArr;
}
test.controller("appControl", function($scope, $http) {
$scope.gridWidth = 4;
$http.get("http://www.w3schools.com/angular/customers.php")
.success(function (response) {
$scope.data = createGrid(Object.keys(response.records).map(function(k) { return response.records[k] }), $scope.gridWidth);
}
);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<body ng-app='app' ng-controller='appControl'>
<input type='text' ng-model='search.Country' />
<table>
<tr ng-repeat="row in data">
<td ng-repeat='work in row'>
{{ work.Country }}
</td>
</tr>
</table>
</body>
You could try something like this:
var test= angular.module("app", []);
test.controller("appControl", function($scope, $http) {
$http.get("http://www.w3schools.com/angular/customers.php")
.success(function (response) {
$scope.data = response.records;
$scope.filteredData= response.records;
}
);
$scope.$watch('search', function () {
var array=[];
for(var i in $scope.data)
{
if($scope.search==undefined || $scope.search.length == 0 || ($scope.data[i].Country!=undefined&&$scope.data[i].Country.toUpperCase().startsWith($scope.search.toUpperCase()))){
array.push($scope.data[i]);
}
}
$scope.filteredData=array;
});
$scope.getFiltered= function(obj, idx){
//Set a property on the item being repeated with its actual index
//return true only for every 1st item in 3 items
return !((obj._index = idx) % 5);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<body ng-app='app' ng-controller='appControl'>
<input type='text' ng-model='search' />
<table>
<tr ng-repeat="work in filteredData | filter:getFiltered | filter:search">
<td>{{work.Country}}</td>
<td ng-show="filteredData[work._index+1]">{{filteredData[work._index+1].Country}}</td>
<td ng-show="filteredData[work._index+2]">{{filteredData[work._index+2].Country}}</td>
<td ng-show="filteredData[work._index+3]">{{filteredData[work._index+3].Country}}</td>
<td ng-show="filteredData[work._index+4]">{{filteredData[work._index+4].Country}}</td>
</tr>
</table>
</body>
You could prefilter the items after a successful Ajax call and every time your model search changes.
Save the previously filtered items into $scope.workers.
Use $scope.$watch to watch for changes in the model search
Use the function searched(data) to filter for the entries that have characters given in the search field using the indexOf method. If the filter is empty, also show every item (typeof $scope.search == 'undefined').
If you want the search be case insensitive, transform searchand the Country .toLowerCase(), when using .indexOf()
Then you will only need one Angular filter $scope.getFiltered(), which makes sure, that the entries are in rows of five.
var test= angular.module("app", []);
test.controller("appControl", function($scope, $http) {
$http.get("http://www.w3schools.com/angular/customers.php")
.success(function (response) {
$scope.data = response.records;
$scope.workers = $scope.searched($scope.data);
}
);
$scope.getFiltered= function(obj, idx){
//Set a property on the item being repeated with its actual index
//return true only for every 1st item in 5 items
return !((obj._index = idx) % 5);
};
$scope.searched = function (data) {
var array = [];
var max = 0;
if (typeof data === 'object') {
max = data.length;
}
for (var i = 0; i < max; i += 1) {
if (typeof $scope.search == 'undefined' || data[i].Country.toLowerCase().indexOf($scope.search.toLowerCase()) != -1) {
array.push(data[i]);
}
}
return array;
};
$scope.$watch('search', function () {
$scope.workers = $scope.searched($scope.data);
})
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<body ng-app='app' ng-controller='appControl'>
<input type='text' ng-model='search' />
<table>
<tr ng-repeat="work in workers | filter:getFiltered">
<td>{{ work.Country }}</td>
<td>{{ workers[$index+1].Country }}</td>
<td>{{ workers[$index+2].Country }}</td>
<td>{{ workers[$index+3].Country }}</td>
<td>{{ workers[$index+4].Country }}</td>
</tr>
</table>
</body>

KnockoutJS or Javascript not keeping proper track of displayed array

I'm writing a paginated table with a page selector at the bottom that displays the different page numbers
I'm using knockout. The numbers are coming from a ko.computed array (self.pages) that calculates how many pages there are based on the number of results / results per page. The problem I'm running into is if the data array is very long and the results per page is set somewhat low, I get something like this:
What I want to do is limit the number of menu items to three, so if page #4 is selected, only items 3,4,5 are visible. Currently I'm implementing a second ko.computed that first retrieves the value of self.pages, then gets the value of the current page number (self.pageNumber), and slices the array so that only 3 items are returned:
self.availablePages = ko.computed(function() {
var pages = self.pages();
var current = self.pageNumber();
if (current === 0) {
return pages.slice(current, current + 3);
} else {
return pages.slice(current - 1, current + 2);
}
});
Now all of this seems to be working fine but there's one bug I have not been able to stamp out. Using the knockout css data-bind, I'm telling it to assign a class of 'selected' to whichever element holds the same value as self.pageNumber (see code below).
If the element selected does not require self.availablePages to change (i.e. selecting 2 when 1 was the previous selection), there are no problems; 2 becomes selected and 1 becomes un-selected.
However, if the selection does require self.availablePages to change (i.e. 1,2,3 visible, selecting 3 will change visible to 2,3,4), the correct numbers display, but instead of 3 being selected, 4 is selected. I'm assuming this is because the index of the array that 3 used to be located at (last) is now being occupied by 4.
Here's the menu:
<ul data-bind="foreach: availablePages">
<li data-bind="if: $index() < 1">
<a data-bind="click: $parent.toFirstPage">First</a>
</li>
<li>
<a data-bind="text: displayValue, click: $parent.goToPage(iterator), css: { selected: $parent.pageNumber() === iterator }"></a>
</li>
<li data-bind="if: $parent.isLastIteration($index)">
<a data-bind="click: $parent.toLastPage">Last</a>
</li>
</ul>
The array being iterated over was originally just an array of numbers, but in trying to fix this bug I changed it to be an array of the following object:
available.MenuModel = function(iterator) {
var self = this;
self.displayValue = iterator + 1;
self.iterator = iterator;
self.isSelected = ko.observable(false);
}
One thing I tried doing was adding the self.isSelected observable to all items in the menu, and then when self.availablePages gets re-computed, the function checks what the pageNumber is and then finds which item in the array matches that and sets self.isSelected(true), and then tried keying the css binding to that.
Unfortunately this did not work; it still has the exact same bug. I've been debugging the script like crazy and there doesn't seem to be an issue; everything seems to know that 3 should be selected, but what's actually selected is 4.
I'm guessing that the knockout bindings aren't smart enough to keep up with this. Is there something I can do or some pattern that would help knockout keep track of which element should be selected? I even tried taking knockout out of it completely, and had a function in the script manually remove/add the 'selected' class whenever self.pageNumber was changed and/or whenever self.availablePages changed but I still got the same issue, so maybe this isn't a knockout issue but something with javascript.
I've tried everything else I can think of; subscribing to various observables, promises, but like I said everything already knows what should be selected so additional checks and callbacks aren't altering anything nor eliminating the bug.
I'm hoping someone will either know the cause/solution of the bug or a smarter way to accomplish the task. This is the self.pages that self.availablePages keys off of, in case that's helpful:
self.pages = ko.computed(function() {
var start = self.totalPages();
var pages = [];
for (var i = 0; i < start + 1; ++i)
pages.push(new available.MenuModel(i));
return pages;
});
This is the entire javascript model (using requireJs):
define(['underscore', 'knockout'], function(_, ko) {
var available = available || {};
available.DynamicResponsiveModel = function(isDataObservable, isPaginated) {
var self = this;
self.workingArray = ko.observableArray([]);
self.backgroundArray = ko.observableArray([]);
self.pageNumber = ko.observable(0);
self.count = function () {
return 15;
}
self.resultsPerPage = ko.observable(self.count());
self.selectResultsPerPage = [25, 50, 100, 200, 500];
self.resultsPerPageOptions = ko.computed(function () {
return self.selectResultsPerPage;
});
self.activeSortFunction = isDataObservable ? available.sortAlphaNumericObservable : available.sortAlphaNumeric;
self.resetPageNumber = function() {
self.pageNumber(0);
}
self.initialize = function(data) {
var sortedList = data.sort(function(obj1, obj2) {
return obj2.NumberOfServices - obj1.NumberOfServices;
});
self.workingArray(sortedList);
self.backgroundArray(sortedList);
self.pageNumber(0);
}
self.intializeWithoutSort = function(data) {
self.workingArray(data);
self.backgroundArray(data);
self.pageNumber(0);
}
self.totalPages = ko.computed(function() {
var num = Math.floor(self.workingArray().length / self.resultsPerPage());
num += self.workingArray().length % self.resultsPerPage() > 0 ? 1 : 0;
return num - 1;
});
self.paginated = ko.computed(function () {
if (isPaginated) {
var first = self.pageNumber() * self.resultsPerPage();
return self.workingArray.slice(first, first + self.resultsPerPage());
} else {
return self.workingArray();
}
});
self.pages = ko.computed(function() {
var start = self.totalPages();
var pages = [];
for (var i = 0; i < start + 1; ++i)
pages.push(new available.MenuModel(i));
return pages;
});
self.availablePages = ko.computed(function() {
var pages = self.pages();
var current = self.pageNumber();
if (current === 0) {
return pages.slice(current, current + 3);
} else {
return pages.slice(current - 1, current + 2);
}
});
self.pageNumDisplay = ko.computed(function() {
return self.pageNumber() + 1;
});
self.hasPrevious = ko.computed(function() {
return self.pageNumber() !== 0;
});
self.hasNext = ko.computed(function() {
return self.pageNumber() !== self.totalPages();
});
self.next = function() {
if (self.pageNumber() < self.totalPages()) {
self.pageNumber(self.pageNumber() + 1);
}
}
self.previous = function() {
if (self.pageNumber() != 0) {
self.pageNumber(self.pageNumber() - 1);
}
}
self.toFirstPage = function() {
self.pageNumber(0);
}
self.toLastPage = function() {
self.pageNumber(self.totalPages());
}
self.setPage = function(data) {
return new Promise(function(resolve, reject) {
self.pageNumber(data);
});
}
self.goToPage = function(data) {
self.pageNumber(data);
}
self.isLastIteration = function (index) {
var currentIndex = index();
var count = self.pages().length;
return currentIndex === count - 1;
}
self.resultsPerPage.subscribe(function() {
self.pageNumber(0);
});
self.filterResults = function (filterFunction) {
self.resetPageNumber();
self.workingArray(filterFunction(self.backgroundArray()));
}
self.resetDisplayData = function() {
self.workingArray(self.backgroundArray());
}
self.updateVisibleResults = function(data) {
self.workingArray(data);
}
}
available.sortAlphaNumericObservable = function () {
//...
}
available.sortAlphaNumeric = function () {
//...
}
return available;
});
Here's the entire table:
<div data-bind="visible: showListOfEquipment, with: availableEquipmentModel">
<section class="panel panel-default table-dynamic">
<table class="primary-table table-bordered">
<thead>
<tr>
<th>
<div class="th">
Part Number
<span class="fa fa-angle-up" data-bind="click: function () { sortByFirstColumn(false); }"></span>
<span class="fa fa-angle-down" data-bind="click: function () { sortByFirstColumn(true); }"></span>
</div>
</th>
<th>
<div class="th">
Serial Number
<span class="fa fa-angle-up" data-bind="click: function () { sortBySecondColumn(false); }"></span>
<span class="fa fa-angle-down" data-bind="click: function () { sortBySecondColumn(true); }"></span>
</div>
</th>
<th>
<div class="th">
Type
<span class="fa fa-angle-up" data-bind="click: function () { sortByThirdColumn(false); }"></span>
<span class="fa fa-angle-down" data-bind="click: function () { sortByThirdColumn(true); }"></span>
</div>
</th>
<th>
<div class="th">
Equipment Group
<span class="fa fa-angle-up" data-bind="click: function () { sortByFourthColumn(false); }"></span>
<span class="fa fa-angle-down" data-bind="click: function () { sortByFourthColumn(true); }"></span>
</div>
</th>
<th>
<div class="th">
Operational
<span class="fa fa-angle-up" data-bind="click: function () { sortByFifthColumn(false); }"></span>
<span class="fa fa-angle-down" data-bind="click: function () { sortByFifthColumn(true); }"></span>
</div>
</th>
<th>
<div class="th">
Valid
<span class="fa fa-angle-up" data-bind="click: function () { sortBySixthColumn(false); }"></span>
<span class="fa fa-angle-down" data-bind="click: function () { sortBySixthColumn(true); }"></span>
</div>
</th>
</tr>
</thead>
<tbody data-bind="foreach: paginated">
<tr>
<td data-bind="text: $data.PartNumber"></td>
<td><a target="_blank" data-bind="text: $data.SerialNumber, click: function () { $root.setSerialNumberAndFindEquipment(SerialNumber) }" style="color:royalblue"></a></td>
<td data-bind="text: $data.Type"></td>
<td data-bind="text: $data.EquipmentGroup"></td>
<td>
<span data-bind="css: $root.operationalCss($data), text: $root.getOpStatus($data)"></span>
</td>
<td data-bind="text: $data.Validity"></td>
</tr>
</tbody>
</table>
<footer class="table-footer">
<div class="row">
<div class="col-md-6 page-num-info">
<span>Show <select style="min-width: 40px; max-width: 50px;" data-bind="options: selectResultsPerPage, value: resultsPerPage"></select> entries per page</span>
</div>
<div class="col-md-6 text-right pagination-container">
<ul class="pagination-sm pagination" data-bind="foreach: pages">
<li data-bind="if: $index() < 1"><a data-bind="click: $parent.toFirstPage">First</a> </li>
<li class="paginationLi"><a data-bind="text: displayValue, click: $parent.goToPage(iterator), css: { selected: isSelected }"></a></li>
<li data-bind="if: $parent.isLastIteration($index)"> <a data-bind="click: $parent.toLastPage">Last</a> </li>
</ul>
</div>
</div>
</footer>
</section>
I went ahead and built a paginator. Instead of using an array as you did, I used just the number of available pages, pageCount.
Probably the only thing worth looking into in more detail is the calculation which pages are to be displayed:
this.visiblePages = ko.computed(function() {
var previousHalf = Math.floor( (this.visiblePageCount() - 1) / 2 ),
nextHalf = Math.ceil( (this.visiblePageCount() - 1) / 2 ),
visiblePages = [],
firstPage,
lastPage;
// too close to the beginning
if ( this.currentPage() - previousHalf < 1 ) {
firstPage = 1;
lastPage = this.visiblePageCount();
if ( lastPage > this.pageCount() ) {
lastPage = this.pageCount();
}
// too close to the end
} else if ( this.currentPage() + nextHalf > this.pageCount() ) {
lastPage = this.pageCount();
firstPage = this.pageCount() - this.visiblePageCount() + 1;
if (firstPage < 1) {
firstPage = 1;
}
// just right
} else {
firstPage = this.currentPage() - previousHalf;
lastPage = this.currentPage() + nextHalf;
}
for (var i = firstPage; i <= lastPage; i++) {
visiblePages.push(i);
}
return visiblePages;
}, this);
Let's go through this piece by piece. We want our current page to be in the middle of all displayed pagination buttons, with some to its left and some to its right. But how many?
If we use an odd number such as three, that's simple: the number minus 1 (the selected one) divided by two. (3 - 1) / 2 = 1, or one to each side.
With an even number of pagination buttons to display, that doesn't work, so we calculate each side individually and round one result up and one result down:
var previousHalf = Math.floor( (this.visiblePageCount() - 1) / 2 ),
nextHalf = Math.ceil( (this.visiblePageCount() - 1) / 2 ),
There are three possible results:
our selection fits
we're too close to the beginning
we're too close to the end
If we're too close to the beginning:
if ( this.currentPage() - previousHalf < 1 ) {
firstPage = 1;
lastPage = this.visiblePageCount();
if ( lastPage > this.pageCount() ) {
lastPage = this.pageCount();
}
}
we start with 1 and try to display pages 1 up to visiblePageCount. If that doesn't work either, because we don't have enough pages, we simply display all we have.
If we're too close to the end:
} else if ( this.currentPage() + nextHalf > this.pageCount() ) {
lastPage = this.pageCount();
firstPage = this.pageCount() - this.visiblePageCount() + 1;
if (firstPage < 1) {
firstPage = 1;
}
}
we end with the last page and try to display as many as we need to the left. If that doesn't work, because we don't have enough pages, we simply display all we have.
Here's the full example:
var ViewModel;
ViewModel = function ViewModel() {
var that = this;
this.pageCount = ko.observable(20);
this.currentPage = ko.observable(1);
this.visiblePageCount = ko.observable(3);
this.gotoPage = function gotoPage(page) {
that.currentPage(page);
};
this.visiblePages = ko.computed(function() {
var previousHalf = Math.floor( (this.visiblePageCount() - 1) / 2 ),
nextHalf = Math.ceil( (this.visiblePageCount() - 1) / 2 ),
visiblePages = [],
firstPage,
lastPage;
if ( this.currentPage() - previousHalf < 1 ) {
firstPage = 1;
lastPage = this.visiblePageCount();
if ( lastPage > this.pageCount() ) {
lastPage = this.pageCount();
}
} else if ( this.currentPage() + nextHalf > this.pageCount() ) {
lastPage = this.pageCount();
firstPage = this.pageCount() - this.visiblePageCount() + 1;
if (firstPage < 1) {
firstPage = 1;
}
} else {
firstPage = this.currentPage() - previousHalf;
lastPage = this.currentPage() + nextHalf;
}
for (var i = firstPage; i <= lastPage; i++) {
visiblePages.push(i);
}
return visiblePages;
}, this);
};
ko.applyBindings( new ViewModel() );
ul {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
margin: 0;
padding: 0;
list-style-type: none;
}
ul li {
-webkit-box-flex: 0;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
}
button {
margin-right: 0.5rem;
padding: 0.5rem;
background-color: lightgrey;
border: none;
}
button.selected {
background-color: lightblue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<ul>
<li><button data-bind="click: gotoPage.bind($data, 1)">First</button></li>
<!-- ko foreach: visiblePages -->
<li>
<button data-bind="text: $data,
click: $parent.gotoPage,
css: { selected: $parent.currentPage() === $data }"></button>
</li>
<!-- /ko -->
<li><button data-bind="click: gotoPage.bind($data, pageCount())">Last</button></li>
</ul>

angular result and pagination doesnt get updated on filter

I have this angular app, where I display a table based on the records, with filters, but the problem is when I eneter a value in filter the records get filter but after removing filter value, it doesnt get updated, and also pagination keeps when we click next numbers keep reducing, I am new to angularjs any help would be greatly appreciated.
This is my html code:
<input type="search" placeholder="Search By Any..." ng-model="search.$" />
table-striped table-bordered">
<thead>
<tr>
<th>User</th>
<th>Content Type</th>
<th>Content Name</th>
<th>Start Time</th>
<th>End Time</th>
<th>Duration(In Secs)</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="record in filteredRecords
| filter: search
| offset: currentPage*itemsPerPage
| limitTo: itemsPerPage
| orderBy:sort:reverse track by $index">
<td>{{record.user}}</td>
<td>{{record.contentType}}</td>
<td>{{record.contentName}}</td>
<td>{{record.startTime}}</td>
<td>{{record.endTime}}</td>
<td>{{record.duration}}</td>
</tr>
</tbody>
<tfoot>
<td colspan="6">
<div class="pagination pull-left">
<ul>
<li ng-class="prevPageDisabled()"><a href
ng-click="prevPage()">« Prev</a></li>
<li ng-repeat="n in range()"
ng-class="{active: n == currentPage}" ng-click="setPage(n)">
{{n+1}}
</li>
<li ng-class="nextPageDisabled()"><a href
ng-click="nextPage()">Next »</a></li>
</ul>
</div>
</td>
</tfoot>
This is angular code:
angular.module("contentViewStatusApp")
.controller("contentViewStatusController", function($scope,
$filter,contentViewStatusService)
{
var records = contentViewStatusService.list();
$scope.changeSort = function(value)
{
if ($scope.sort == value)
{
$scope.reverse = !$scope.reverse;
return;
}
$scope.sort = value;
$scope.reverse = false;
}
$scope.itemsPerPage = 8;
$scope.currentPage = 0;
$scope.filteredRecords = [];
$scope.items = [];
$scope.range = function()
{
var rangeSize = 5;
var ret = [];
var start;
start = $scope.currentPage;
if (start > $scope.pageCount() - rangeSize && $scope.pageCount() > rangeSize)
{
start = $scope.pageCount() - rangeSize + 1;
}
if($scope.pageCount() > rangeSize)
for (var i = start; i < start + rangeSize; i++)
{
ret.push(i);
}
else
for (var i = start; i < $scope.pageCount()+1; i++)
{
ret.push(i);
}
return ret;
};
var filterBy = $filter('filter');
$scope.$watch('search', function(newValue)
{
$scope.filteredRecords = filterBy(records, newValue);
}, true);
$scope.prevPage = function()
{
if ($scope.currentPage > 0)
{
$scope.currentPage--;
}
};
$scope.prevPageDisabled = function()
{
return $scope.currentPage === 0 ? "disabled" : "";
};
$scope.pageCount = function()
{
return Math.ceil($scope.filteredRecords.length / $scope.itemsPerPage) - 1;
};
$scope.nextPage = function()
{
if ($scope.currentPage < $scope.pageCount())
{
$scope.currentPage++;
}
};
$scope.nextPageDisabled = function()
{
return $scope.currentPage === $scope.pageCount() ? "disabled" : "";
};
$scope.setPage = function(n)
{
$scope.currentPage = n;
};
});
#Mckenzie, for your scenario already plugin available where you can get more out of it.
Check out this plugin: ng-table ( https://github.com/esvit/ng-table )
See this example it does all your job that you require.
ng-table combining sorting and filtering

Categories