Useful navigation bindings can be developed by using methods such as pager.getParentPage
,
and pager.Page.prototype.getFullRoute
.
On this page we'll develop one such binding: page-step:
.
The goal of the binding is to be able to use it like
<a data-bind="page-step: -1">Go back and read about {0}</a> <a data-bind="page-step: 1">Next page</a>
where page-step: -1
indicates that the link should point at the
previous page and page-step: 1
indicates that the link should
point at the next page. If you look at the buttons below you'll see that they use this binding!
The page-step:
binding needs to
a
-tag can be done using
pager.getParentPage
. Navigating to the sibling pages can be done using
pager.Page#children
-observable array and pager.Page#parentPage
.
Using pager.Page.prototype.nullObject
it is possible to make
the reference robust for race conditions.
pager.Page.prototype.getFullRoute
and pager.Page.prototype.val
using 'title' as argument.
We start by developing a method to find the sibling
// return the index of the current page var indexOfCurrentPage = function(page) { return page.parentPage.children.indexOf(page); }; // returns a sibling page, or the nullObject-page if no sibling exists at the moment var pageStep = function(page, step) { return ko.computed(function() { var rawStep = ko.utils.unwrapObservable(step); return page.parentPage.children()[(indexOfCurrentPage(page)) + rawStep] || page.nullObject; }); };
that now can be used in a new class:
// new constructor var NextPage = function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { this.element = element; this.bindingContext = bindingContext; this.path = ko.observable(); var parent = pager.getParentPage(bindingContext); this.siblingPage = pageStep(parent, valueAccessor()); };
Now we got a reference to the page (this.sublingPage
). Next we need to calculate the absolute path to
the page.
// this line already exists this.siblingPage = pageStep(parent, valueAccessor()); // calculate the path to the sibling page as a computed observable this.path = ko.computed(function () { var value = this.siblingPage(); return value.getFullRoute()().join('/'); }, this); };
Let us bind the path and title to the element.
NextPage.prototype.bind = function () { // Here we bind the text inside the a-tag var text = $(this.element).text(); ko.computed(function () { var page = this.siblingPage(); if (text.length === 0) { // Use the page title as text if no text is supplied inside the a-tag var newText = page.val('title'); ko.applyBindingsToNode(this.element, { 'text':newText }); } else if (text.indexOf('{0}')) { // Search replace {0} with the page title var replacedText = text.replace('{0}', page.val('title')); ko.applyBindingsToNode(this.element, { 'text':replacedText }); } }, this); // Here we bind the path to the sibling page var hash = ko.computed(function () { return pager.Href.hash + this.path(); }, this); ko.applyBindingsToNode(this.element, { attr:{ 'href':hash } }); };
Finally we register the binding using KnockoutJS.
// register the binding as page-step ko.bindingHandlers['page-step'] = { init:function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { // call the constructor in order to setup element, bindingContext and and siblingPage var nextPage = new NextPage(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext); // bind href and text nextPage.bind(); } };
If you inspect the source code of the navigation buttons you'll see that this binding is used for their navigation.