Skip to content

Instantly share code, notes, and snippets.

@joelnet
Created June 3, 2011 18:03
  • Star 92 You must be signed in to star a gist
  • Fork 39 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save joelnet/1006808 to your computer and use it in GitHub Desktop.
Unobtrusive Knockout support library for jQuery
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>
/**
* @preserve Unobtrusive Knockout support library for jQuery
*
* @author Joel Thoms
* @version 1.1
*/
(function($) {
if (!$ || !$['fn']) throw new Error('jQuery library is required.');
/**
* Private method to recursively render key value pairs into a string
*
* @param {Object} options Object to render into a string.
* @return {string} The string value of the object passed in.
*/
function render(options) {
var rendered = [];
for (var key in options) {
var val = options[key];
switch (typeof val) {
case 'string': rendered.push(key + ':' + val); break;
case 'object': rendered.push(key + ':{' + render(val) + '}'); break;
case 'function': rendered.push(key + ':' + val.toString()); break;
}
}
return rendered.join(',');
}
/**
* jQuery extension to handle unobtrusive Knockout data binding.
*
* @param {Object} options Object to render 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 = render(opts);
if (attr != null && attr != '') {
$(this)['attr']('data-bind', attr);
}
});
};
})(jQuery);
/*
Unobtrusive Knockout support library for jQuery
@author Joel Thoms
@version 1.1
*/
(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);
@DavidDeSloovere
Copy link

Well done!

@kingbuzzman
Copy link

Performance overhead?

@joelnet
Copy link
Author

joelnet commented Aug 26, 2011

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

@Osoascam
Copy link

Osoascam commented Sep 1, 2011

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

@joelnet
Copy link
Author

joelnet commented Sep 1, 2011

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.

@Osoascam
Copy link

Osoascam commented Sep 1, 2011

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

@joelnet
Copy link
Author

joelnet commented Sep 1, 2011

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
Copy link

jwize commented May 22, 2012

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);

@joelnet
Copy link
Author

joelnet commented May 23, 2012

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
Copy link

jwize commented May 23, 2012 via email

@joelnet
Copy link
Author

joelnet commented May 23, 2012

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

@jwize
Copy link

jwize commented May 23, 2012 via email

@joelnet
Copy link
Author

joelnet commented May 23, 2012

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
Copy link

jwize commented May 23, 2012 via email

@jwize
Copy link

jwize commented May 23, 2012 via email

@jwize
Copy link

jwize commented May 24, 2012 via email

@joelnet
Copy link
Author

joelnet commented May 24, 2012

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
Copy link

jwize commented May 24, 2012 via email

@joelnet
Copy link
Author

joelnet commented May 24, 2012

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
Copy link

jwize commented May 24, 2012 via email

@unirgy
Copy link

unirgy commented Jun 26, 2013

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

Is there a reason for this notation?

@joelnet
Copy link
Author

joelnet commented Sep 13, 2016

@unirgy, there is no valid reason for that notation. I would do this differently today. Not sure what the reason was way back then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment