-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Bindings uri templates and uri query params
NickLeippe edited this page Feb 10, 2015
·
7 revisions
You want to specify the target url in the href
of an <a>
tag but tie specific query parameters to observables. The alternatives are:
- specify the
href
via theattr
binding and build the entire url string by hand inline or via helper function - use the
click
binding instead which can be more verbose both inline and with extra routing code, which also precludes client-side history api routing benefits (at least not without extra, IMO superfluous work)
These handlers modify:
- the
href
attribute on<a>
tags - the
src
attribute on<img>
tags - the
action
attribute on<form>
tags
- requires URI.js
- does not require optional jquery pieces of URI.js
- urlt requires the template functionality of URI.js--choose accordingly when building your download
- could be rewritten to work with miuri.js and/or some other URI-template library without trouble
<ul data-bind="foreach: mylist">
<li><a href="/page?foo=bar" data-bind="urlq: { id: id }">edit</a>
<span data-bind="text: description"></span></li>
</ul>
Produces href="/page?foo=bar&id=123"
<ul data-bind="foreach: mylist">
<li><a href="/page/{foo}{?bar}" data-bind="urlt: { foo: fooValue, bar: barValue }">edit</a>
<span data-bind="text: description"></span></li>
</ul>
Produces href="/page/abc?bar=xyz"
/*****************************************************************************
*
* helper used by both urlq and urlt binding handlers
*
****************************************************************************/
function urlAttrForTagName(element) {
//var tagName = ko.utils.tagNameLower(element);
var tagName = element && element.tagName && element.tagName.toLowerCase();
if (tagName == "img") {
return 'src';
} else if (tagName == "a") {
return 'href';
} else if (tagName == "form") {
return 'action';
} else {
return;
}
}
/*****************************************************************************
*
* urlq KO binding handler
*
* Purpose:
* Control a url's query parameters for href attribute of <a> tags, src
* attribute of <img> tags, action attribute of <form> tags
*
* Dependencies:
* Requires URI.js from: http://medialize.github.com/URI.js/
*
* Usage:
* data-bind="urlq: { foo: fooValue, bar: barValue }"
*
* Will synchronize ?foo=abc&bar=xyz portion of url in src/href attribute
* to the respective observables. (one-way binding)
*
* Only replaces query parameters explicitly specified--any others in the
* attribute's original value are unchanged.
*
* Interaction with an overlapping attr binding handler is undefined.
* I *think* it will always reapply after the attr handler makes changes,
* but am not sure.
*
* Getting this to play nicely with attr: { href/src: } may be difficult.
*
****************************************************************************/
ko.bindingHandlers.urlq = {
update: function(element, valueAccessor) {
var value;
var paramName;
var paramValue;
var url;
var attr = urlAttrForTagName(element);
if (!attr) {
return;
}
url = URI(element.getAttribute(attr));
value = ko.utils.unwrapObservable(valueAccessor()) || {};
for (paramName in value) {
if (typeof paramName != "string") {
continue;
}
paramValue = ko.utils.unwrapObservable(value[paramName]);
url.removeSearch(paramName);
if (paramValue !== null &&
paramValue != undefined)
{
url.addSearch(paramName, paramValue);
}
}
element.setAttribute(attr, url.toString());
}
};
/*****************************************************************************
*
* urlt KO binding handler
*
* Purpose:
* Control a url's value using uri templates
* see: http://medialize.github.com/URI.js/uri-template.html
*
* Dependencies:
* requires URI.js from: http://medialize.github.com/URI.js/ with the
* optional uri template features enabled
*
* Usage:
* data-bind="urlt: { foo: fooValue, bar: barValue }"
*
* Will synchronize src/href="page/{foo}{?bar}" -> page/abc?bar=xyz
* via the respective observables. (one-way binding)
*
* Probably incompatible with overlapping attr binding handler (since this
* needs to save the original template--it would need to have its init
* called again if the original value got replaced, not just update)
*
* Getting this to play nicely with attr: { href/src: } may be difficult, if
* possible at all.
*
****************************************************************************/
var url_template_saved = '__ko__urlt_template_saved';
ko.bindingHandlers.urlt = {
init: function(element) {
var attr = urlAttrForTagName(element);
if (!attr) {
return;
}
element[url_template_saved] = element.getAttribute(attr);
},
update: function(element, valueAccessor) {
var value;
var attr = urlAttrForTagName(element);
if (!attr) {
return;
}
value = ko.toJS(ko.utils.unwrapObservable(valueAccessor()) || {});
element.setAttribute(attr,
URI.expand(element[url_template_saved], value).toString());
}
};