Progressive Enhancement KnockoutJS Paging
by Daniel Lamb
HTML
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<b>Search Results</b>
<p>Progressive enhancement with KnockoutJS Paging</p>
<div class="pages">
<a href="/page/1">1</a>
<a href="/page/2">2</a>
<a href="/page/3">3</a>
<a href="/page/4">4</a>
<a href="/page/5">5</a>
<a href="/page/6">6</a>
<a href="/page/7">7</a>
<a href="/page/8">8</a>
<a href="/page/9">9</a>
<a href="/page/10">10</a>
</div>
<!-- The first page is rendered server side -->
<div id="list" data-bind="foreach: list">
<div data-bind="PE, text: name">Listing 0</div>
<div data-bind="PE, text: name">Listing 1</div>
<div data-bind="PE, text: name">Listing 2</div>
<div data-bind="PE, text: name">Listing 3</div>
<div data-bind="PE, text: name">Listing 4</div>
<div data-bind="PE, text: name">Listing 5</div>
<div data-bind="PE, text: name">Listing 6</div>
<div data-bind="PE, text: name">Listing 7</div>
<div data-bind="PE, text: name">Listing 8</div>
<div data-bind="PE, text: name">Listing 9</div>
</div>
CSS
#list div {
background-color: #eee;
margin: 5px;
height: 100px;
}
JavaScript
//Custom binding to load the values into the view model
ko.bindingHandlers.PE = {
init: function(element, valueAccessor, allBindings) {
var bindings = allBindings();
if (typeof bindings.text === 'function') {
bindings.text($(element).text());
}
}
};
// listing model
var Listing = function (listing) {
this.name = ko.observable('');
// set initial values if provided
if (listing) {
this.name(listing.name);
}
};
var getInitialData = function(results) {
// create a list of empty listings
var children = $('#list').children(),
// create a list of listings
list = _.range(children.length)
.map(function (i) {
var elm = $(children[i]),
listing = new Listing();
// get listing values from the DOM
ko.applyBindings(listing, elm[0]);
// convert the model to JSON
listing = ko.toJS(listing);
// update the max id used so far
results.maxId += 1;
// remove all but the first element
if(elm.index() > 0)
{
elm.remove();
}
return listing;
});
return list;
}
// search results view
var ResultsView = function () {
var self = this;
self.maxId = 0;
self.list = ko.observableArray(getInitialData(self));
self.pendingRequest = ko.observable(false)
};
var results = new ResultsView();
ko.applyBindings(results);
// listen for page clicks
$('.pages').on('click', 'a', function (e) {
var link = e.target,
pg = parseInt(link.href.substr(link.href.indexOf('/page/')+6));
results.maxId = (pg * 10) - 10;
getNextItems(10);
return false;
});
function...