Created

Embed URL

HTTPS clone URL

SSH clone URL

You can clone with HTTPS or SSH.

Download Gist

Unobtrusive Knockout support library for jQuery

View example.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
Choose a ticket class: <select id="tickets"></select>
 
<p id="ticketOutput"></p>
<script id="ticketTemplate" type="text/x-jquery-tmpl">
{{if chosenTicket}}
You have chosen <b>${ chosenTicket().name }</b>
($${ chosenTicket().price })
<button data-bind="click: resetTicket">Clear</button>
{{/if}}
</script>
<script type="text/javascript">
var viewModel = {
tickets: [
{ name: "Economy", price: 199.95 },
{ name: "Business", price: 449.22 },
{ name: "First Class", price: 1199.99 }
],
chosenTicket: ko.observable(),
resetTicket: function() { this.chosenTicket(null) }
};
$("#tickets").dataBind({
options: "tickets",
optionsCaption: "'Choose...'",
optionsText: "'name'",
value: "chosenTicket"
});
$("#ticketOutput").dataBind({ template: "'ticketTemplate'" });
ko.applyBindings(viewModel);
</script>
View example.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
/**
* @preserve Unobtrusive Knockout support library for jQuery
*
* @author Joel Thoms
* @version 1.0
*/
 
(function($) {
 
if (!$ || !$['fn']) throw new Error('jQuery library is required.');
 
/**
* Private method to recursively parse key value pairs into a string
*
* @param {Object} options Object to parse into a string.
* @return {string} The string value of the object passed in.
*/
function parse(options) {
var parsed = [];
for (var key in options) {
var val = options[key];
switch (typeof val) {
case 'string': parsed.push(key + ':' + val); break;
case 'object': parsed.push(key + ':{' + parse(val) + '}'); break;
case 'function': parsed.push(key + ':' + val.toString()); break;
}
}
return parsed.join(',');
}
 
/**
* jQuery extension to handle unobtrusive Knockout data binding.
*
* @param {Object} options Object to parse into a string.
* @return {Object} A jQuery object.
*/
$['fn']['dataBind'] = $['fn']['dataBind'] || function(options) {
return this['each'](function() {
var opts = $.extend({}, $['fn']['dataBind']['defaults'], options);
var attr = parse(opts);
if (attr != null && attr != '') {
$(this)['attr']('data-bind', attr);
}
});
};
 
})(jQuery);
View example.html
1 2 3 4 5 6 7 8
/*
Unobtrusive Knockout support library for jQuery
@author Joel Thoms
@version 1.0
*/
 
(function(a){function e(a){var b=[],c;for(c in a){var d=a[c];switch(typeof d){case "string":b.push(c+":"+d);break;case "object":b.push(c+":{"+e(d)+"}");break;case "function":b.push(c+":"+d.toString())}}return b.join(",")}if(!a||!a.fn)throw Error("jQuery library is required.");a.fn.dataBind=a.fn.dataBind||function(f){return this.each(function(){var b=a.extend({},a.fn.dataBind.defaults,f),b=e(b);b!=null&&b!=""&&a(this).attr("data-bind",b)})}})(jQuery);

Well done!

Performance overhead?

Owner

there should be no performance overhead. once the code has executed, knockout works in the same exact way as it normally would.

But them this allows for unobtrusive data binding OUTSIDE templates, right? On templates this won't do anything...

Owner

Correct. The jQuery templates are not supported. Only native Knockout code. Additional code would have to be added to support the jQuery templates. The project doesn't require jQuery templates, so I haven't created any code that works with them.

Yeah, additional code per template framework. Can't think of an agnostic way of dealing with templates. Thanks, joelnet!

Owner

It might be possible to attach the data-binding after the template has been rendered. but again, I haven't been able to test it.

jwize commented

Some selectors don't seem to work or maybe if you can't bind the same element twice. In that case wouldn't it be good to add an overload dataBind(options, options2) or better yet, in pseudo-code, dataBind(params [] options) where the other options could be merged? That way you could share options between objects.

var sharedOptions = {
someBinding : "binding logic",
moreBindings : "more binding logic"
}

$("#obj1").dataBind( {
... // some options.
},
sharedOptions);

Owner
joelnet commented

you are right. can't bind twice. no options to merge. this should be done outside the library.

There's a couple of options to do merging here:
http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically

jwize commented
Owner
joelnet commented

I'd create a custom combine function that can handle all of this that is separate from the data-bind.

jwize commented
Owner
joelnet commented

Ahhhh ok. I get where you were coming from now.

You can even do things like this:

$('#tickets').dataBind({
    event: {
        focus: function(evt) {
            alert('focus!');
        },
        change: function(evt) {
            alert('change!');
        }
    }
});
jwize commented
jwize commented
jwize commented
Owner
joelnet commented

what isn't working? what is "fullName"? Is that a text field? if so, I don't believe "load" is a valid event. Can you use chrome's element inspector to look inside "fullName" to see what the data-bind attribute generated is?

http://www.threetipsaday.com/2008/12/debug-inspect-google-chrome-inspector/

jwize commented
Owner
joelnet commented

I don't believe the $(this) part will work. use the element inspector and you'll see why. The function get's converted into a string and will lose the "this" context.

jwize commented
unirgy commented

I think there's an inconsistency: instead of $.extend() need to use $['extend']() /sarcasm.

Is there a reason for this notation?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.