DEMO
I'am developing a Pagination Functionality using handlebars js and fetching the data from JSON.
First 5 results will be shown on page load.
on click of next pagination another set of 5 results will be displayed and so on.
If i have total number of results 100 by displaying each 5 Results in page. Page Numbers will be 1 of 20.
if the pagination has more then 5 number of Pages , I want to display "1,2,3 ... last Page Number (20)" same vice versa
on load Previous Button Should be hidden when ever next page is clicked it has to be enabled.
Request to you please look into this and give some advice / suggestion on this.
Should be Like Below
Appreciate your kind help !
Thanks
some code sample :
$(function () {
var opts = {
pageMax: 5,
postsDiv: $('#posts'),
dataUrl: "searchResult.json"
}
function range(i) { return i ? range(i - 1).concat(i) : [] }
function loadPosts(posts) {
opts.postsDiv.empty();
posts.each(function () {
var source = $("#post-template").html();
var template = Handlebars.compile(source);
var context = {
title: this.title,
desc: this.body,
};
var html = template(context);
opts.postsDiv.append(html);
});
}
function paginate(pageCount) {
var source = $("#pagination-template").html();
var template = Handlebars.compile(source);
var context = { pages: range(pageCount) };
var html = template(context);
opts.postsDiv.after(html);
function changePage(pageNumber) {
pageItems.removeClass('active');
pageItems.filter('[data-page="' + pageNumber + '"]').addClass('active');
loadPosts(data.slice(pageNumber * opts.pageMax - opts.pageMax, pageNumber * opts.pageMax));
}
var pageItems = $('.pagination>li.pagination-page');
pageItems.on('click', function () {
changePage(this.getAttribute('data-page'));
}).filter('[data-page="1"]').addClass('active');
$('.pagination>li.pagination-prev').on('click', function () {
gotoPageNumber = parseInt($('.pagination>li.active').attr('data-page')) - 1;
if (gotoPageNumber <= 0) { gotoPageNumber = pageCount; }
changePage(gotoPageNumber);
});
$('.pagination>li.pagination-next').on('click', function () {
gotoPageNumber = parseInt($('.pagination>li.active').attr('data-page')) + 1;
if (gotoPageNumber > pageCount) { gotoPageNumber = 1; }
changePage(gotoPageNumber);
});
}
$.ajax({
dataType: 'json',
url: opts.dataUrl,
success: function (response_json) {
data = $(response_json.records.page);
dataCount = data.length;
pageCount = Math.ceil(dataCount / opts.pageMax);
if (dataCount > opts.pageMax) {
paginate(pageCount);
posts = data.slice(0, opts.pageMax);
} else {
posts = data;
}
loadPosts(posts);
}
});
});
I had to solve a similar issue a few months ago. I found this Gist from kottenator.
Your range function is modified thusly, with c being the current page, and m your pageCount. Calls to the function have been modified a bit and a recursive call to your paginate(...) function is also added to recompute the tag after navigation (also, a branch was added to your DOM appending function calls, to modify the pagination tag, I used a ternary operator. There may be more elegant to achieve this).
See this CodePen
function range(c,m) {
var current = c || 1,
last = m,
delta = 2,
left = current - delta,
right = parseInt(current) + delta + 1,
range = [],
rangeWithEllipsis = [],
l,
t;
range.push(1);
for (var i = c - delta ; i <= c + delta ; i++) {
if (i >= left && i < right && i < m && i > 1) {
range.push(i);
}
}
range.push(m);
for (var i of range) {
if (l) {
if (i - l === 2) {
t = l+1;
rangeWithEllipsis.push(t);
} else if (i - l !== 1) {
rangeWithEllipsis.push("...");
}
}
rangeWithEllipsis.push(i);
l = i;
}
return rangeWithEllipsis;
}
It doesn't solve exactly your problem per say, but it does paginate correctly.
If I have some time, I'll try and make it paginate the exact way you want to (it's really only about customizing the delta, left and right operand in the algorithm, and changing your pagination next and pagination prev event handler calls).
Edit I changed the algorithm to find the left and right boundary. Your index.html is also modified a bit.
The idea is to compute the left and right boundary by multiples of 5. You then create a range of the indexes to show and add an elipsis if the difference is too big. This should effectively solves your original problem.
JavaScript
getFirstDigits = (t) => {
return parseInt(t.toString().slice(0,-1))
}
getLastDigit = (t) => {
return parseInt(t.toString().slice(-1))
}
isMultipleOf5 = (t) => {
return [0,5].reduce((res,curr)=>{
return res = res || curr === getLastDigit(t);
},false);
}
isBetween0and5 = (t) => {
const _t = getLastDigit(t);
return _t < 5;
}
isBetween5and9 = (t) => {
const _t = getLastDigit(t);
return _t => 5 && _t <= 9;
}
appendDigit = (t,d) => {
return parseInt(getFirstDigits(t).toString() + d.toString())
}
getSecondRightMostDigit = (t) => {
return parseInt(t.toString().slice(-2,-1))
}
incrementSecondDigit = (t) => {
return t+10;
}
getLeft = (t) => {
if(t>=10){
if(isBetween0and5(t)) return appendDigit(t,0);
else return appendDigit(t,5);
} else {
if (t<5) return 0;
else return 5;
}
}
getRight = (t) => {
if(t<5) return 5;
else if (t<10) return 10;
else if(isBetween0and5(t)) return appendDigit(t,5)
else return appendDigit(incrementSecondDigit(t),0);
}
function range(c,m) {
var current = c || 1,
last = m,
delta = 2,
left = getLeft(c),
right = getRight(c),
range = [],
rangeWithEllipsis = [],
l,
t;
var rightBoundary = right < 5 ? 5 : right;
for (var i = left ; i < rightBoundary ; ++i) {
if( i < m && i > 0) range.push(i);
}
range.push(m);
for (var i of range) {
if (l) {
if (i - l === 2) {
t = l+1;
rangeWithEllipsis.push(t);
} else if (i - l !== 1){
rangeWithEllipsis.push("...");
}
}
rangeWithEllipsis.push(i);
l = i;
}
return rangeWithEllipsis;
}
HTML/HandleBars
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Handlebars Pagination</title>
<link href="main.css" rel="stylesheet" />
<script src="jquery.min.js"></script>
<script src="handlebars.min.js"></script>
<script src="functions.js"></script>
</head>
<body class="container">
<div id="posts"></div>
<script id="pagination-template" type="text/x-handlebars-template">
<ul class="pagination">
<li class="pagination-prev">«</li>
{{#each pages}}
<li class="pagination-page" data-page="{{this}}">{{this}}</li>
{{/each}}
<li class="pagination-next">»</li>
</ul>
</script>
<script id="post-template" type="text/x-handlebars-template">
<div class="score-structural score-column2-wideright search-listings post">
<div class="score-right">
<h4>{{record_count}}</h4>
<h5 style="z-index: 1;">
{{ title }}
</h5>
<p style="z-index: 1;"> {{ desc }} </p>
</div>
</div>
<hr>
</script>
</body>
</html>
<script>
$(function () {
var opts = {
pageMax: 2,
postsDiv: $('#posts'),
dataUrl: "searchResult.json"
}
function loadPosts(posts) {
opts.postsDiv.empty();
posts.each(function () {
var source = $("#post-template").html();
var template = Handlebars.compile(source);
var context = {
title: this.title,
desc: this.body,
};
var html = template(context);
opts.postsDiv.append(html);
});
hidePrev();
}
function hidePrev() { $('.pagination .pagination-prev').hide(); }
function showPrev() { $('.pagination .pagination-prev').show(); }
function hideNext() { $('.pagination .pagination-next').hide(); }
function showNext() { $('.pagination .pagination-next').show(); }
function paginate(page,pageCount) {
var source = $("#pagination-template").html();
var template = Handlebars.compile(source);
var context = { pages: range(page,pageCount) };
console.log(range(page,pageCount));
var html = template(context);
var paginationTag = opts.postsDiv.parent().find(".pagination");
paginationTag.length > 0 ? paginationTag.replaceWith(html) : opts.postsDiv.before(html);
function changePage(page) {
pageItems.removeClass('active');
pageItems.filter('[data-page="' + page + '"]').addClass('active');
loadPosts(data.slice(page * opts.pageMax - opts.pageMax, page * opts.pageMax));
paginate(page,pageCount);
if (gotoPageNumber <= 1) {
hidePrev();
}
}
var pageItems = $('.pagination>li.pagination-page');
var pageItemsLastPage = $('.pagination li').length - 2;
pageItems.removeClass('active');
pageItems.filter('[data-page="' + page + '"]').addClass('active');
pageItems.on('click', function () {
getDataPageNo = this.getAttribute('data-page')
console.log(getDataPageNo)
changePage(getDataPageNo);
if (getDataPageNo == 1) {
hidePrev()
}
else if (getDataPageNo == pageItemsLastPage) {
hideNext();
}
else {
showPrev();
showNext();
}
});
$('.pagination>li.pagination-prev').on('click', function () {
gotoPageNumber = parseInt($('.pagination>li.active').attr('data-page')) - 1;
changePage(gotoPageNumber);
});
$('.pagination>li.pagination-next').on('click', function () {
gotoPageNumber = parseInt($('.pagination>li.active').attr('data-page')) + 1;
if (gotoPageNumber > pageCount) {
gotoPageNumber = 1;
showPrev();
}
changePage(gotoPageNumber);
});
}
$.ajax({
dataType: 'json',
url: opts.dataUrl,
success: function (response_json) {
data = $(response_json.records.page);
dataCount = data.length;
pageCount = Math.ceil(dataCount / opts.pageMax);
if (dataCount > opts.pageMax) {
paginate(1,pageCount);
posts = data.slice(0, opts.pageMax);
} else {
posts = data;
}
loadPosts(posts);
}
});
});
</script>
Related
This is bothering my mind for a few weeks now. I have a working example of some Javascript that updates the DOM within a loop.
But I can't get this "trick" to work for my real code.
This is the link to my Plunk: https://plnkr.co/edit/oRf6ES74TJatPRetZEec?p=preview
The HTML:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css">
<link rel="stylesheet" href="style.css">
<script src="http://code.jquery.com/jquery-1.12.4.js"></script>
<script src="http://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="js/script.js"></script>
<script src="js/script2.js"></script>
<script src="js/json-util.js"></script>
<script src="js/scorito-dal.js"></script>
<script src="js/matching.js"></script>
</head>
<body>
<div id="log">..log here..</div>
<button onclick="doHeavyJob();">do heavy job</button>
<button onclick="doHeavyJobJquery();">do heavy job with jQuery</button>
<button onclick="match();">match</button>
</body>
</html>
The script in js/script.js, which is called from the button "do heavy job" works:
async function doHeavyJob() {
$('#log').html('');
for (var j=0; j<10; j++) {
await sleep(1000)
console.log('Iteration: ' + j);
(function (j) {
setTimeout(function() { // pause the loop, and update the DOM
var logPanel = document.getElementById('log');
var txt = 'DOM update' + j;
logPanel.innerHTML += txt + ', ';
}, 0);
})(j);
}
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
The exact same code does not work within a jQuery each(), this is appearantly due to jQuery, and easy to work around by using the for-loop. If you're interested, check script2.js in my plunk.
My real script in js/matching, which is called from the button "match" does NOT work.
var _keeperCombinations = [];
var _defenderCombinations = [];
var _midfielderCombinations = [];
var _attackerCombinations = [];
var _mostPoints = 0;
var _bestCombination = [];
function updateStatus(keeperCount, ixKeeper, msg) {
msg = '%gereed: ' + Math.round(ixKeeper / keeperCount * 100, 0);
console.log(msg);
$('#log').html(msg);
}
function match() {
$('#log').html('');
updateStatus(1, 1, 'Match started');
var playersData = scoritoDal.getPlayersData();
this.determineKeepers(playersData);
this.determineDefenders(playersData);
this.determineMidfielders(playersData);
this.determineAttackers(playersData);
var keeperCount = _keeperCombinations.length
for (var ixKeeper=0; ixKeeper<keeperCount; ixKeeper++) {
var keepers = _keeperCombinations[ixKeeper];
doMatching(keepers, keeperCount, ixKeeper);
}
if (_bestCombination.length === 0) {
alert('Er kon geen beste combinatie worden bepaald');
}
else {
alert('Ready: ' + _bestCombination.toString());
$(_bestCombination).each(function(ix, playerId) {
});
}
}
/*
* Match 2 keepers, 5 defenders, 6 midfielders and 5 attackers
* First pick the 5 best keepers, 10 best defenders, 12 best midfielders and 10 best attackers.
* 3 / 2 (k), 7 / 3 (d & a) and 8 / 4 (m) >> most points / most points per prices
*/
var _confirmed = false;
function doMatching(keepers, keeperCount, ixKeeper) {
// Make a new promise
let p = new Promise(
// The executor function is called with the ability to resolve or reject the promise
(resolve, reject) => {
for (var ixDefenders=0; ixDefenders<_defenderCombinations.length; ixDefenders++) {
var defenders = _defenderCombinations[ixDefenders];
for (var ixMidfielders=0; ixMidfielders<_midfielderCombinations.length; ixMidfielders++) {
var midfielders = _midfielderCombinations[ixMidfielders];
for (var ixAttackers=0; ixAttackers<_attackerCombinations.length; ixAttackers++) {
var attackers = _attackerCombinations[ixAttackers];
procesPlayers(keepers, defenders, midfielders, attackers);
}
}
resolve(ixDefenders);
}
});
p.then(
function(ixDefenders){
console.log('LOG: ' + keeperCount + " - " + ixKeeper);
updateStatus(keeperCount, ixKeeper);
}
);
}
async function procesPlayers(keepers, defenders, midfielders, attackers) {
var totals = calculateTotals(keepers, defenders, midfielders, attackers);
// check if total price is within budget
if (totals.TotalPrice <= 56500000) {
if (totals.TotalPoints > _mostPoints) {
_mostPoints = totals.TotalPoints;
setBestCombination(keepers, defenders, midfielders, attackers);
}
}
}
function calculateTotals(keepers, defenders, midfielders, attackers) {
var playerIds = [];
var totalPoints = 0;
var totalPrice = 0;
var allPlayers = keepers.concat(defenders, midfielders, attackers);
var checkTeams = [];
$(allPlayers).each(function(ix, player){
var club = checkTeams.find(t => t.Name === player.Club);
if (!club) {
club = {"Name":player.Club, "Count":1};
checkTeams.push(club);
}
else club.Count++;
if (club.Count > 4) {
totalPoints = 0;
return false;
}
playerIds.push(player.ID);
var factor = 1;
if (player.Position === 'Keeper' && ix > 0) factor = 0.5; // 2nd keeper gets less points
if (player.Position === 'Defender' && ix > 2) factor = 0.5; // 4th defender gets less points
if (player.Position === 'Midfielder' && ix > 3) factor = 0.5; // 5th midfielder gets less points
if (player.Position === 'Attacker' && ix > 2) factor = 0.5; // 4th attacker gets less points
var playerPoints = player.Points * factor;
totalPoints += playerPoints;
totalPrice += player.Price;
});
return {"TotalPoints":totalPoints,"TotalPrice":totalPrice};
}
function determineKeepers(playersData) {
console.log('Determining keepers');
$('#progres').text('Determine 5 best keepers');
var bestKeepers = this.determineBestPlayers(playersData, 'Keeper', 3, 2); // 3, 2
if (bestKeepers && $(bestKeepers).length > 0) {
// now determine all combinations
this.determineCombinations(bestKeepers, 2);
_keeperCombinations = this._combinations;
}
}
function determineDefenders(playersData) {
console.log('Determining defenders');
$('#progres').text('Determining 10 best defenders');
var bestDefenders = this.determineBestPlayers(playersData, 'Defender', 5, 3); // 6, 3
if (bestDefenders && $(bestDefenders).length > 0) {
// now determine all combinations
this.determineCombinations(bestDefenders, 5);
_defenderCombinations = this._combinations;
}
}
function determineMidfielders(playersData) {
console.log('Determining midfielders');
$('#progres').text('Determine 12 best midfielders');
var bestMidfielders = this.determineBestPlayers(playersData, 'Midfielder', 5, 3); // 7, 3
if (bestMidfielders && $(bestMidfielders).length > 0) {
// now determine all combinations
console.log('*** Determining all midfielder combinations ***');
this.determineCombinations(bestMidfielders, 6);
_midfielderCombinations = this._combinations;
}
}
function determineAttackers(playersData) {
console.log('Determining attackers');
$('#progres').text('Determine 10 best attackers');
var bestAttackers = this.determineBestPlayers(playersData, 'Attacker', 5, 3); // 6, 3
if (bestAttackers && $(bestAttackers).length > 0) {
// now determine all combinations
this.determineCombinations(bestAttackers, 5);
_attackerCombinations = this._combinations;
}
}
/*
* Determine the best players for a position
* - position: Keeper|Defender|Midfielder|Attacker
* - byPoints: the nr of best players by points
* - byFactor: the nr of best players by factor
*/
function determineBestPlayers(playersData, position, byPoints, byFactor) {
var players = $.grep(playersData, function(p) {return p.Position === position});
var playersByPoints = players.sort(jsonUtil.sortByProperty('Points', true));
var bestPlayersByPoints = playersByPoints.slice(0, byPoints);
var playersByFactor = players.sort(jsonUtil.sortByProperty('Factor', true));
var bestPlayersByFactor = playersByFactor.slice(0, byFactor);
// players could be in both lists, make it unique
var bestPlayers = jsonUtil.joinArrays(bestPlayersByPoints, bestPlayersByFactor, true, 'ID');
// if not the nr wanted, add extra players
// take theze by turn on points / on factor
var cnt = $(bestPlayers).length;
var nextByPoints = true;
var cntExtra = 0;
while (cnt < byPoints + byFactor) {
cntExtra++;
var isOK = false;
while (!isOK) {
var ix=0; // if the next player is already chosen, move to the next
var extraPlayer = {};
if (nextByPoints) {
extraPlayer = playersByPoints[byPoints+cntExtra+ix-1];
}
else {
extraPlayer = playersByFactor[byFactor+cntExtra+ix-1];
}
if (!bestPlayers.find(x => x.ID === extraPlayer.ID)) {
bestPlayers.push(extraPlayer);
isOK = true;
}
else {
ix++;
}
}
nextByPoints = !nextByPoints;
cnt++;
}
bestPlayers = bestPlayers.sort(jsonUtil.sortByProperty('Points', true)); // add the end we want the players sorted by total points
console.log('Best player for position ' + position);
console.log(bestPlayersToString(bestPlayers));
return bestPlayers;
}
function bestPlayersToString(bestPlayers) {
var result = '';
var sep = '';
$(bestPlayers).each(function(ix, player) {
result += sep;
result += JSON.stringify(player);
if (sep === '') sep = ',';
});
return result;
}
function setBestCombination(keepers, defenders, midfielders, attackers) {
_bestCombination = [];
$(keepers).each(function(ix, keeper){
_bestCombination.push(keeper.ID);
});
$(defenders).each(function(ix, defender){
_bestCombination.push(defender.ID);
});
$(midfielders).each(function(ix, midfielder){
_bestCombination.push(midfielder.ID);
});
$(attackers).each(function(ix, attacker){
_bestCombination.push(attacker.ID);
});
}
/* Combinations */
var _combinations = [];
var _curCombination = [];
function determineCombinations(players, cnt) {
_combinations = [];
_curCombination = [];
this.addCombinations(players, cnt);
}
/*
* Take 2 from 5 (keepers), 5 from 10 (defenders, attackera) or 6 from 12 (midfielders)
* It is a rather complex recursive method with nested iterations over the
* (remaining players).
*/
var _curLevel = 1;
function addCombinations(players, cnt) {
for (var i=0; i<players.length; i++) {
var player = players[i];
_curCombination.push(player);
if (_curCombination.length == cnt) {
_combinations.push(_curCombination.slice(0)); // slicing creates a clone
//console.log(curCombinationToString());
_curCombination.pop();
continue;
}
var curPlayers = players.slice(i+1);
_curLevel++;
addCombinations(curPlayers, cnt);
};
_curCombination.pop();
_curLevel--;
}
function curCombinationToString() {
var result = '';
var sep = '';
$(_curCombination).each(function(ix, player) {
result += sep;
result += JSON.stringify(player);
if (sep === '') sep = ',';
});
return result;
}
Any ideas will be greatly appreciated!!!!
finally got this to work, after numerous, NUMEROUS attempts.
And off course I'd like to share this with you.
Here's the essence to getting this to work:
the loop must be inside an async function
the code with the logic for 1 loop must be inside a Promise
the code also needs to be within setTimeout
the call for this logic must be 'annotated' with await
So this works:
'use strict';
async function testPromise() {
var msg;
for (var ix=1; ix<10; ix++) {
msg = 'Before loop #'+ix
$('#log').html(msg);
await doActualWork();
msg = 'After loop #'+ix
$('#log').html(msg);
}
}
/*
* Perform logic for one loop
*/
function doActualWork() {
return new Promise(
function (resolve, reject) {
window.setTimeout(
function () {
// do the actual work like calling an API
resolve();
}, 100);
}
);
}
I want to automatic go through random items from the DropDownList1.
It's working, but it's going by the order first to last, and I want to go through items randomly.
/* function to automatic select DropDownList1 items */
function selectFromDropdown(selector, text) {
$(selector).find('option').each(function() {
if ($(this).text() == text) {
$(selector).val($(this).val());
return false;
}
})
}
$(document).ready(function() {
let numberOfTimes = 0;
const time = 1000 //3s
let values = [];
$('#DropDownList1').find('option').each(function() {
values.push($(this).text())
});
console.log(values);
const interval = setInterval(function() {
selectFromDropdown('#DropDownList1', values[numberOfTimes])
if (numberOfTimes == values.length - 1) {
clearInterval(interval);
} else {
numberOfTimes = numberOfTimes + 1;
}
},
time);
});
Here the snnipet: https://jsfiddle.net/lucasangelo_/17Lgr0kc/6/
If you want to get random values from a select, then you can use the next function:
function getRandomValuesFromSelect(selector, numberOfItemsWanted)
{
var valuesSelected = [];
var childrenSelect = document.getElementById(selector).children;
for (var i = 0; i < numberOfItemsWanted; i++) {
var randomValue = Math.floor(Math.random() * childrenSelect.length);
var randomOption = childrenSelect[randomValue];
if (valuesSelected.indexOf(randomOption.value) < 0) {
valuesSelected.push(randomOption.value);
} else {
i--;
}
}
return valuesSelected;
}
Then you could call it like so:
getRandomValuesFromSelect("DropDownList1", 3);
The answer is:
/* function to automatic select DropDownList1 items */
function selectFromDropdown(selector, text) {
$(selector).find('option').each(function() {
if ($(this).text() == text) {
$(selector).val($(this).val());
return false;
}
})
}
function getRandomNumber(min, max) {
return (Math.random() * (max - min) + min).toFixed(0);
}
$(document).ready(function() {
let numeroDeVezes = 0;
const tempoEntreCadaChamada = 1000 //3s
let valores = [];
$('#DropDownList1').find('option').each(function() {
valores.push($(this).text())
});
console.log(valores);
const interval = setInterval(function() {
const randomNumber = getRandomNumber(0, valores.length - 1);
const randomItem = valores[randomNumber];
//console.log(randomItem);
selectFromDropdown('#DropDownList1', randomItem),
console.log(`${numeroDeVezes} - Chamou do PostBack para ${randomItem}`);
//__doPostBack('LButton3', 'OnClick');
if (numeroDeVezes == valores.length - 1) {
console.log("Percorreu todos, mata o setInterval");
clearInterval(interval);
} else {
numeroDeVezes = numeroDeVezes + 1;
}
},
tempoEntreCadaChamada);
});
Thank you boys!
In trying to learn more about JavaScript patterns I created the following 2 "Meters" based on an example I found. Is Meter1 better because it uses the Observable Property (or so I think) pattern? Would this pattern be used real-world or not really because of all the frameworks available?
//Meter1 - Observable Property Pattern
var Meter1 = function (count) {
var countChanging = [],
countChanged = [];
this.count = function (val) {
if (val !== undefined && val !== count) {
for (var i = 0; i < countChanging.length; i++) {
if (!countChanging[i](this, val)) {
return count;
}
}
count = val;
for (var i = 0; i < countChanged.length; i++) {
countChanged[i](this);
}
}
return count;
};
this.increment = function () {
return count = count + 1;
}
this.onCountChanging = function (callback) {
countChanging.push(callback);
};
this.onCountChanged = function (callback) {
countChanged.push(callback);
};
};
var meter = new Meter1(5);
var btnClick = document.getElementById('btnClick');
btnClick.addEventListener('click', function () {
meter.count(meter.count() + 1);
}, false);
meter.onCountChanging(function (b, count) {
if (count > 10) {
document.getElementById('numClicks').innerHTML = "Enough already!!!!!";
return false;
}
return true;
});
meter.onCountChanged(function () {
document.getElementById('numClicks').innerHTML = "Test " + meter.count();
});
//Meter2 - Does the same thing, is Observable Property pattern better?
var Meter2 = function (count) {
this.count = 0;
};
var meter2 = new Meter2(5);
var btnClick2 = document.getElementById('btnClick2');
btnClick2.addEventListener('click', function () {
meter2.count = meter2.count + 1;
if (meter2.count > 10) {
document.getElementById('numClicks2').innerHTML = "Enough already!!!!!";
} else {
document.getElementById('numClicks2').innerHTML = "Test " + meter2.count;
}
}, false);
<body>
<div id="numClicks">Test</div>
<div id="numClicks2">Test</div>
<button id="btnClick">Click - Meter</button>
<button id="btnClick2">Click - Meter 2</button>
</body>
</html>
<script src="../js/generalTesting.js"></script>
Hey Guys,
I am trying to split my responses from my RSS feed reader up into pages. I'm doing this by populating a feedList array with RSS Items, and taking 2 items at a time to display and placing them in a displayList array connected to an ng-repeat. When you click next page it will update the displayList array with 2 new items from the feedArray.
I got a simplified version running with an array of names http://jsfiddle.net/halfasleep/ZJFke/4/
When I tried to implement it with the feed reader code, it doesn't populate the displayList array before trying to display it, when you hit "Next Page" it will drop some items into the displayList array and start to display it (though for some reason only moving 1 item at a time instead of 2). Any help would be appreciated!
JS Fiddle: http://jsfiddle.net/WDL8U/
Html
<html ng-app="FeedReader">
<head>
<meta charset="UTF-8">
<title>Reader Trial</title>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular-resource.js"></script>
<link rel="stylesheet" href="FeedReader.css" />
</head>
<body ng-controller="blogCtrl">
There are {{feedList.length}} entries.
<ul class="blogPosts" ng-class="listClass" ng-init="populate();">
<li ng-repeat="list in displayList"><h2><a ng-click="publish(list.content);">{{list.title}}</a></h2><p>{{list.contentSnippet}}</p><span> - {{list.author}}</span></li>
</ul>
<div class="blogPosts fullWidth" style="height: 500px; overflow: scroll;" ng-hide="noCont" ng-bind-html="htmlContent"></div>
<div style="clear: both;">
<a ng-click="prev()" ng-hide="hideP">Previous Page</a>
<a ng-click="next()" ng-hide="hideN">Next Page</a>
</div>
<script src="./feedReader2.js"></script>
</body>
</html>
JS
var feedList = [];
var displayList = [];
var app = angular.module('FeedReader',['ngResource']);
app.factory('feedLoader',['$resource', function($resource){
var googleAPI = $resource('http://ajax.googleapis.com/ajax/services/feed/load',{},{
collect: {
method: 'JSONP',
params: {
v: '1.0',
callback: 'JSON_CALLBACK'
}
}
});
return googleAPI;
}]);
app.service('createFeedList',['feedLoader', function(feedLoader){
this.get = function(){
var feed = {
feedName: 'Slashdot',
feedURL: 'http://rss.slashdot.org/Slashdot/slashdot'
};
feedLoader.collect({
q: feed.feedURL,
num: 10
},{},function(result){
var feed = result.responseData.feed.entries;
for(i = 0; i < feed.length; i++){
feedList.push(feed[i]);
}
});
return feedList;
}
}]);
app.controller('blogCtrl',['$scope', '$sce', 'createFeedList', function($scope, $sce, createFeedList){
$scope.feedList = createFeedList.get();
// Page Code
$scope.hideP = true;
var begin = 0;
var end = 2;
$scope.displayList = [$scope.feedList[begin],$scope.feedList[end]];
$scope.next = function(){
if(end <= $scope.feedList.length - 2){
begin += 2;
end += 2;
$scope.displayList = [$scope.feedList[begin],$scope.feedList[end]];
$scope.hideP = false;
}
if(end == $scope.feedList.length-1) {
$scope.hideN = true;
}
}
$scope.prev = function(){
if(end == 3){
$scope.hideP = true;
}
if(end >= 1) {
begin -= 2;
end -= 2;
$scope.displayList = [$scope.feedList[begin],$scope.feedList[end]];
$scope.hideN = false;
}
}
// Display Content Code
$scope.noCont = true;
$scope.listClass = 'fullWidth';
$scope.publish = function(cont) {
$scope.noCont = false;
$scope.htmlContent = $sce.trustAsHtml(cont);
$scope.listClass = 'halfWidth';
}
}]);
You need to return a promise and then populate $scope.feedList when the data is ready. Then you can perform actions on the other $scope variables as well. Give this a try:
var feedList = [];
var displayList = [];
var app = angular.module('FeedReader',['ngResource']);
app.factory('feedLoader',['$resource', function($resource){
var googleAPI = $resource('http://ajax.googleapis.com/ajax/services/feed/load',{},{
collect: {
method: 'JSONP',
params: {
v: '1.0',
callback: 'JSON_CALLBACK'
}
}
});
return googleAPI;
}]);
app.service('createFeedList',['feedLoader', '$q', function(feedLoader, $q){
this.get = function(){
var feed = {
feedName: 'Slashdot',
feedURL: 'http://rss.slashdot.org/Slashdot/slashdot'
};
var deferred = $q.defer();
feedLoader.collect({
q: feed.feedURL,
num: 10
},{},function(result){
var feed = result.responseData.feed.entries;
for(i = 0; i < feed.length; i++){
feedList.push(feed[i]);
}
deferred.resolve(feedList);
});
return deferred.promise;
}
}]);
app.controller('blogCtrl',['$scope', '$sce', 'createFeedList', function($scope, $sce, createFeedList){
//$scope.feedList = createFeedList.get();
createFeedList.get().then(function(data) {
$scope.feedList = data;
$scope.displayList = [$scope.feedList[begin],$scope.feedList[end]];
$scope.next = function(){
if(end <= $scope.feedList.length - 2){
begin += 2;
end += 2;
$scope.displayList = [$scope.feedList[begin],$scope.feedList[end]];
$scope.hideP = false;
}
if(end == $scope.feedList.length-1) {
$scope.hideN = true;
}
}
$scope.prev = function(){
if(end == 3){
$scope.hideP = true;
}
if(end >= 1) {
begin -= 2;
end -= 2;
$scope.displayList = [$scope.feedList[begin],$scope.feedList[end]];
$scope.hideN = false;
}
}
});
// Page Code
$scope.hideP = true;
var begin = 0;
var end = 2;
// Display Content Code
$scope.noCont = true;
$scope.listClass = 'fullWidth';
$scope.publish = function(cont) {
$scope.noCont = false;
$scope.htmlContent = $sce.trustAsHtml(cont);
$scope.listClass = 'halfWidth';
}
}]);
Hope that helps.
I need to perform a pagination where I need to display 5 rows every time. First 5 rows are displayed. On the second click, the next 5, the third rows no. 11-15, and so on. I need to display even if at least one row is there (after n clicks where totalRows mod 5 is less than 5). I do not get this.
function rightArrow()
{
pageCount = document.getElementById('pgCnt').value; //totalRows / 5
totCount = document.getElementById('totCnt').value; //totalRows
currPage = tempRows - pageCount + 2; //current set of rows
document.getElementById('tempPage').value = tempPage;
var m = totCount%5;
if(pageCount != tempRows)
m = 5;
else
{ m = (totCount % 5) - 1;
document.getElementById('rightArr').disabled = true;
}
document.getElementById('pgCnt').value = document.getElementById('pgCnt').value - 1;
for(i = 0; i < m;i++)
{
$.ajax({
type: "POST",
url: "getEODRow.php",
data: "page=" + currPage + "&row=" + i,
success: function(html)
{
var row = document.getElementById('chRecommend').insertRow(0);
temp = html.split(",");
for(j = 0; j < 9; j++)
{
str = temp[j].replace("\"","");
str = temp[j].replace("\"",'');
str = temp[j].replace("[",'');
col = row.insertCell(j);
col.innerHTML = str;
}
}
});
}
currPage++;
}
I realize this question is pretty much dead, but I came across it looking for something else, and I figured I'd toss an answer at it. Since it's not a critical issue, I went ahead and reworked everything you've got here plus a lot of code that you didn't choose to include.
// App namespace
var MyApp = {Paging: {}};
// Paging Controller
MyApp.Paging.Controller = function(){ this.init.apply(this,arguments);};
MyApp.Paging.Controller.prototype =
{
// Initializer gets everything hooked up
init: function()
{
$.log("Initializing Paging Controller.")
// Get all the nodes we'll need
this.totCntNode = document.getElementById("totCnt");
this.pgCntNode = document.getElementById("pgCnt");
this.currMinNode = document.getElementById("currMin");
this.currMaxNode = document.getElementById("currMax");
this.prevPageButton = document.getElementById("prevPageButton");
this.nextPageButton = document.getElementById("nextPageButton");
this.pageContainer = document.getElementById("pageOfItems");
// Mimic table's .insertRow to make row adding easy
this.pageContainer.insertRow = function() {
var row = document.createElement("div");
row.className = "row clearfix";
for (var i = 0; i < MyApp.Paging.Model.itemsPerRow; i++)
{
var cell = document.createElement("span");
row.appendChild(cell);
}
this.appendChild(row);
return row;
};
// Attach listeners to the next and previous buttons
this.prevPageButton.onclick = this.showPrevPage.bind(this);
this.nextPageButton.onclick = this.showNextPage.bind(this);
// Update the display for the first time
this.updatePageInfo();
},
// Run this whenever the model has changed and needs to update the display
updatePageInfo: function()
{
// Get info about what page we're on
var currentPage = MyApp.Paging.Model.currentPage,
totalPages = MyApp.Paging.Model.getTotalPages(),
itemCount = MyApp.Paging.Model.itemCount,
pageSize = MyApp.Paging.Model.getPageSize(),
rowsPerPage = MyApp.Paging.Model.rowsPerPage,
rowsOnThisPage = Math.ceil(MyApp.Paging.Model.getItemsOnPage(currentPage)/MyApp.Paging.Model.itemsPerRow);
// Clear out previous page data
while (this.pageContainer.children.length > 0)
{
this.pageContainer.removeChild(this.pageContainer.children[0]);
}
// Add space for the new page data
for (var rowInd = 0; rowInd < rowsOnThisPage ; rowInd++)
{
this.pageContainer.insertRow();
}
$.log("Loading Page " + currentPage + ".");
// Request the data via ajax for each row
for(var i = 0; i < rowsOnThisPage ; i++)
{
$.ajax({
type: MyApp.Paging.Model.queryType,
url: MyApp.Paging.Model.queryURI,
data: MyApp.Paging.Model.getQueryData(currentPage, i),
success: function(pageNum, rowNum, result)
{
// Don't serve data from the wrong page
if (pageNum !== MyApp.Paging.Model.currentPage) return;
// When we get the data back, put it into the correct container
// regardless of when it was received
var row = this.pageContainer.children[rowNum],
temp = result.replace(/[\["]/g,"").split(","),
str = "";
for(var j = 0; j < temp.length; j++)
{
row.children[j].innerHTML = temp[j];
}
}.bind(this, currentPage, i)
});
}
// Update the informational bits under the items
this.totCntNode.textContent = itemCount;
this.pgCntNode.textContent = totalPages;
var min = currentPage * (pageSize ) + 1;
this.currMinNode.textContent = min;
this.currMaxNode.textContent = Math.min(min + pageSize - 1, itemCount);
// Disable the prev page button if there are no previous pages
if (currentPage <= 0)
{
if (this.prevPageButton.className.indexOf("disabled") < 0)
{
this.prevPageButton.className += " disabled";
}
}
// Enable the prev page button if there are previous pages
else
{
if (this.prevPageButton.className.indexOf("disabled") > -1)
{
this.prevPageButton.className = this.prevPageButton.className.replace(/(?:^|\s+)disabled(?!\S)/g, "");
}
}
// Disable the next page button if there are next pages
if (currentPage + 1 >= totalPages)
{
if (this.nextPageButton.className.indexOf("disabled") < 0)
{
this.nextPageButton.className += " disabled";
}
}
// Enable the next page button if there are next pages
else
{
if (this.nextPageButton.className.indexOf("disabled") > -1)
{
this.nextPageButton.className = this.nextPageButton.className.replace(/(?:^|\s+)disabled(?!\S)/g, "");
}
}
},
// This is called when the next page button is clicked.
showNextPage: function()
{
if (MyApp.Paging.Model.currentPage + 1 >= MyApp.Paging.Model.getTotalPages())
{
// Shouldn't have been able to activate this anyway
}
else
{
MyApp.Paging.Model.currentPage++;
this.updatePageInfo();
}
return false;
},
// This is called when the prev page button is clicked
showPrevPage: function()
{
if (MyApp.Paging.Model.currentPage <= 0)
{
// Shouldn't have been able to activate this anyway
}
else
{
MyApp.Paging.Model.currentPage--;
this.updatePageInfo();
}
return false;
}
};
// I typically expect an object like this to be created by the server and dropped dynamically onto the page
MyApp.Paging.Model = {
itemCount: 140,
itemsPerRow: 9,
rowsPerPage: 5,
currentPage: 0,
queryType: "POST",
queryURI: "getEODRow.php",
queryDataFormat: "page={itemPage}&row={itemRow}",
getTotalPages: function() {
with(MyApp.Paging.Model) {
return Math.ceil(itemCount/(itemsPerRow*rowsPerPage));
}
},
getPageSize: function() {
with(MyApp.Paging.Model) {
return itemsPerRow * rowsPerPage;
}
},
getItemsOnPage: function(pageNum) {
with(MyApp.Paging.Model) {
return Math.min(((pageNum+1) * getPageSize()), itemCount) - pageNum*getPageSize();
}
},
getItemsInRow: function(pageNum, rowNum) {
with(MyApp.Paging.Model) {
var onPage = getItemsOnPage(pageNum);
return Math.min((rowNum+1)*itemsPerRow, onPage) - rowNum*itemsPerRow;
}
},
getQueryData: function(itemPage, itemRow) {
with(MyApp.Paging.Model) {
var data = queryDataFormat;
data = data.replace(/{itemPage}/gi, itemPage);
data = data.replace(/{itemRow}/gi, itemRow);
return data;
}
}
};
So, whenever the page is loaded, with an onload handler or whatever, you would create and hang onto a singleton instance of the Paging Controller.
MyApp.Paging.Controller.instance = new MyApp.Paging.Controller();
This should handle issues with Ajax calls returning out of order as well as partial pages of data and partial rows within those pages.
Demo: http://jsfiddle.net/2UJH8/8/