Last month, I blogged about how much I love KnockoutJS, a fantastic JavaScript MVVM framework for creating rich, dynamic UIs. In that post, I also shared my only real qualms with Knockout: the exclusive use of data-bind attributes for binding a view to the underlying ViewModel. And while there is much about the declarative style bindings Knockout uses that I like, I thought it would be a fun (and hopefully useful) exercise to take my original unobtrusive post and try to formalize my thinking into a plugin. Derick Bailey deserves the credit for much of the inspiration here, so be sure to check out the awesome work he’s doing with the Backbone.ModelBinding plugin.

Knockout.Unobtrusive :: Unobtrusive model binding syntax for KnockoutJS

Over the last week, I’ve spent some time here and there taking the code from my original post and using it as inspiration to create a plugin for Knockout that allows developers to use script-based bindings, rather than the default data-bind syntax. The result is Knockout.Unobtrusive, which I’ve placed on GitHub. I should note here that using Knockout.Unobtrusive isn’t the only option if you’re looking to clean up some of the clutter in your data-bind attributes. Ryan Niemeyer has a great post on cleaning up Knockout Views that I recommend you read. But, if you’re looking to go farther, read on!

This plugin was developed using a test-first approach, with qUnit as my JavaScript test framework of choice. The core plugin itself was authored using CoffeeScript, and the full source can be found here.

CoffeeScript, if you’re not familiar, is a language that compiles 1:1 to JavaScript. I’ve been using it for several weeks now, and I’m sold. During development, I’m running the CoffeeScript compiler locally, with instructions to watch the above coffee file for changes, and to re-compile that file into JavaScript on each change. The resulting JavaScript file is then run against my qUnit tests.

Even if you’re not familiar with CoffeeScript,  the code at the link above should be pretty easy to mentally parse. If, however, you would rather take a look at the JavaScript that CoffeeScript generates, you can check it out here in the source for Knockout.Unobtrusive.

Getting Started with Knockout.Unobtrusive

Getting started with the plugin is pretty easy. First, download the source from my GitHub repository and include a reference to knockout.unobtrusive.js just below Knockout proper.

Note: If you cannot view the embedded Gist above for any reason, click here to view the embedded code.

Once you’ve added Knockout.Unobtrusive to your application, you can start moving your existing bindings (data-bind) into a bindings object, and/or create new bindings as needed. I have a full example up on JSFiddle, and embedded below, but let’s take a look at some of the HTML from that fiddle.

Note: If you cannot view the embedded Gist above for any reason, click here to view the embedded code.

Similar to the example code used in my original post, Knockout.Unobtrusive ties the View (markup) and ViewModel together through the use of id or name attributes in my markup, and a bindings object passed into the plugin. Using the id and name properties above, we simply need to create a bindings object that “glues together” my View and ViewModel. We can do so by listing pairs of DOM element ids/names and ViewModel properties, organized by Knockout binding type (value, text, options, etc.).

Note: If you cannot view the embedded Gist above for any reason, click here to view the embedded code.

You’ll notice a couple of conventions at work here. Each property in the bindings object corresponds to a binding provided by KnockoutJS. Thus, “text,” “value,” “options” and the like are intended to be leveraged in similar situations as the default bindings. Where I previously had an input field similar to the following:

<input type=”text” data-bind=”value: name” ></input>

I would instead do something like this in my HTML:

<input type=”text” id=”name”></input>

Or even this:

<input type=”text” name=”name”></input> //name AND id are supported

and add the following to my JavsScript bindings object:

var bindings = {

  value: [ { name: ‘name’ } ]

};

Of course, Knockout.Unobtrusive uses several conventions, so anytime you have an exact match between an element id/name—like we do above—you can minimize the binding declaration like so:

var bindings = {

  value: [ ‘name’ ]

};

Mixing and matching minimized and expanded properties works, too:

var bindings = {

value: [ ‘name’, { twitterHandle: ‘handle’ } ]

};

Note that Knockout.Unobtrusive doesn’t paint you into a script-only corner. Since the current version is effectively creating data-bind attributes on your behalf, using the bindings you specify, there’s nothing stopping you from using data-bind attributes along with script-based bindings. In fact, v0.1 of Knockout.Unobtrusive doesn’t yet support the creation of bindings in template blocks, so you’ll need to continue to use data-bind for the time being.

Once I’ve defined my bindings object, and I’ve adjusted my markup with id or name attributes where appropriate, I need to call Knockout.Unobtrusive’s createBindings() method and pass in my bindings object. Of course, this needs to be done before I call ko.applyBindings()

Note: If you cannot view the embedded Gist above for any reason, click here to view the embedded code.

Now I’m off to the races, using KnockoutJS with clean views and all of my behavior defined in JavaScript! Here’s a working sample, courtesy of JSFiddle.

Note: If you cannot view the embedded Fiddle above for any reason, click here to view it at JSFiddle.net.

For more information about Knockout.Unobtrusive, visit the GitHub repository here. The Readme goes into a bit more detail about currently supported bindings, conventions used in the plugin and plans for future features, so be sure to check that out. Keep in mind that this plugin is v0.1, so there’s still plenty of work to do. So if you have any questions, comments, issues or suggestions, feel free to leave them here as comments, or enter a issue or two at the project site.

So, what do you think? Am I onto something, or totally off the reservation? How would you improve this plugin, or what else do I need to add?

Tagged with:
 
  • http://profiles.google.com/rniemeyer Ryan Niemeyer

    Hi Brandon-
    Nice work! I really like the direction that you are going with this plugin.  As far as unobstrusive options go, creating the binding adapter object is a really nice alternative to a bunch of lines of  “wire-up” code for each binding.   

    I think that the next step would be adding support for templates, as they are generally a necessity when dealing with arrays.  I have a few thoughts about ways to handle templates and will send some ideas your way.

    Also, I would recommend to people using this plugin that they keep the binding object strictly decoupled from their view model and still work to make their view model robust enough that the object remains simple.  Just because the bindings are being applied unobtrusively doesn’t mean that putting strings like “items().length < 10" in the binding object is a good idea.

    You are definitely onto something!  

  • http://www.userinexperience.com Brandon Satrom

    Ryan,

    Thanks for the feedback!  I think you are right about adding template support next, as its kind of a must for this plugin to be legitimate. I’ve been thinking about how to do that for the past few days, and would love to hear your ideas.

    Excellent suggestions on keeping the binding object decoupled. You’re post about minimizing magic strings like “items().length < 10" is very valid in any case. 

  • Aaron Powell

    Hey Brandon, it seems that great minds think alike because today I published a similar plugin concept! http://www.aaron-powell.com/javascript/knockoutjs-preparser

    You’re obviously using an object to define a ‘schema’ for your viewModel, does it not concern you that you’re ending up with viewModel + a supporting object which is very close to matching your viewModel in structure?

    My only concern with your approach (and it’s also a problem I’m trying to solve in mine still) is that you still have JSON-esq strings in your binding, even if the binding is in JavaScript and not the DOM. Admittedly I don’t have a “good way” to solve that problem yet :P .

  • http://www.alvinashcraft.com/2011/08/10/dew-drop-august-10-2011/ Dew Drop – August 10, 2011 | Alvin Ashcraft's Morning Dew

    [...] Introducing the Knockout.Unobtrusive Plugin (Brandon Satrom) [...]

  • http://www.userinexperience.com Brandon Satrom

    @google-5d608d8fe9156b0e01b3f224e28409b0:disqus Very nice!
    The bindings object is really more of an adapter object, but you are correct that I have an additional object to manage, and that was an early concern for me. However, IMO it is an improvement over data-bind attributes scattered all over my view. I attempted to minimize the repetition by supporting minimized declarations “text: [ 'name' ]” instead of “text: [ { name: "name" } ],” but it would be an interesting exercise to take that convention even further and auto-map ViewModel properties to View elements. I’ve added that to the list, and we can discuss further here: https://github.com/bsatrom/Knockout.Unobtrusive/issues/1

    As for the JSON strings, I don’t have a problem with those. With JavaScript being a dynamic language, string manipulation is normal. My bigger concern with getting data-* out of my Views is for cases where I am essentially describing behavior in my markup. It’s onclick all over again, and even though there are some great ways to minimize the amount of behavior defined in the markup (as Ryan mentions below and in his response here: http://userinexperience.com/?p=633) I’m still looking for a model that allows me to use KO and define 100% of my bindings and behavior in JavaScript. There’s a lot I really like about your approach, but in moving from data-bind to data-ko-*, it just seems like you’ve shifted the string manipulation problem to the left. :)

    That said, I really like the way that you’ve structured the plugin to work with no action on the user’s part, other than to include the library. I think that’s a nice approach. I also like the way you’re integrating into the ko.applyBindings method. I considered doing something similar, but didn’t want to go there until the idea was more baked in my head.

  • http://www.userinexperience.com Brandon Satrom

    @google-5426db8fdf8dbbec1644b378175a2d9f:disqus FYI, I created an issue on the project site re: adding template support. https://github.com/bsatrom/Knockout.Unobtrusive/issues/2 Feel free to add any thoughts or ideas there, and we can spitball some concepts.

  • Richard Marriott

    Hi,

    +1 to the convention based mapping based on VM properties. We use a similar approach in an SL app with Caliburn.Micro and it does help to keep a nice clean view.

    I’ll try the plugin out (which looks v nice btw!) on my current app that’s using knockout js and come back with any thoughts I have..:-)

  • http://www.userinexperience.com Brandon Satrom

    Thanks, Richard! Looking forward to hearing your thoughts!

  • Aaron Powell

    I’ve actually got basic ‘schema binding’ support baked into my library so you could do:

    ko.applyBindings(viewModel, schema, node);

    Ultimately I hadn’t gone further down that path as I hadn’t solved the problem in my head of how to produce a good schema for the viewModel bindings.

    I agree that I’ve not truly solved the problem as I’ve just gone from a single data-bind to multiple data-ko-* attributes but ultimately I wanted to keep it as close ‘normal’ knockout for people who are familiar with that but at the same time addressing the primary concern I have with it.

    Would you be interested in rolling the two projects into one so for an unobtrusive concept there’s a single plugin and it can allow either a schema or split attributes?

  • http://blog.cwa.me.uk/2011/08/11/the-morning-brew-914/ The Morning Brew – Chris Alcock » The Morning Brew #914

    [...] Introducing the Knockout.Unobtrusive Plugin – Brandon Satrom shares Knockout.Unobtrusive, a plugin for the KnockoutJS framework which makes it possible to represent your binding in code rather than declarativly using data-bind attributes. The plugin was written using CoffeeScript to generate the actual JavaScript implementation, and has a suite of unit tests written in qUnit. [...]

  • http://blueonionsoftware.com/blog.aspx?p=5a9c5886-a117-4ff0-ad11-e29b289a12f5 Friday Links #165 | Blue Onion Software *

    [...] C# Async examples in F# – Part 1 | //TODO: – Chris Marinos' Blog Incandescent Software: A comprehensive list of resources about JavaScript Travis CI Wekeroad — The BackboneJS and Knockout Danceoff Introducing the Knockout.Unobtrusive Plugin | User InExperience [...]

  • http://www.userinexperience.com Brandon Satrom

    @google-5d608d8fe9156b0e01b3f224e28409b0:disqus,
    Sure! I’m certainly open to trying to unify things into one plugin with some clear conventions and opinions, but plenty of configuration hooks. Only caveat I would have is that I’d like to keep working in CoffeeScript.

    Do you want to fork Knockout.Unobtrusive as the starting point? Or how would you like to do things? :D

  • Brian O’Connell

    I’m a tad confused here as I’m not that familiar with Knockout.js but I am assuming that knockout is the one that adds these attributes after the page has been downloaded and the script has kicked in on the client. If that is the case then I don’t see the problem.

    The essence of unobtrusive scripting to me is graceful degredation. In simple terms if I want a delete button to show a modal confirmation dialog I will make the button a link to a delete confirmation page so that if the user has script off or no access to script then they get the link. Then I add some script to layer on or override this to do it the modal way for users with script on. My whole approach to scripting is enhancement of an already working system.

    Now obviously you may make a decision before developing a system that you will not be catering for those without script or you control the user environment (intranet) so it does not warrant consideration.

    Either way if I have script off I get html presumably with no strange attributes because knockout or whatever framework I am using is not running. If I have script on then I get all of these things and I don’t care because if script is on it will all work. My job as a developer is to deliver clean working html that doesn’t care if script is running or not. Then I can chose to have some script try and run on top of that not caring if it can or not.

    I don’t see the issue with what that script does. Comparing what knockout puts in the attribute to the old is not comparable as the issue here was that that button won’t work if I have script off – thats the elegance I seek from Unobtrusive scripting. Web Applications that work with or without script.

  • http://www.userinexperience.com Brandon Satrom

    Brian,First, to correct your assumption, knockout itself does not, in fact add the data-bind attributes to your page automatically. As a developer, you have to do that yourself, and knockout then reads these attributes to add observables and behavior to your page.I’d also respectfully submit that what you posit as the essence of unobtrusive JavaScript is a positive side-benefit of the practice, yet tangential. The primary objective of Unobtrusive JavaScript is to separate the *behavior* of an application from its presentation. In essence, it means that I separate all semblance of behavior from my markup and segregate that into script blocks, or better still, separate script files.This, of course, aids in Graceful Degradation because, as a developer, the behavior of my page is centrally defined in a single script and can serve a direction toward places in my page or application that require script-less functionality, should I deem it necessary.That’s my primary motive for Knockout.Unobtrusive (and why I’ve named it as such): to create a plugin that allows me to use KO, which is a great framework, while still keeping all behavior where it belongs, in script.To be fair, nether the plugin, nor KO itself, have much to say about graceful degradation  That’s still the responsibility of the developer. That said, I believe my plugin makes GD easier because, as I said above, it encourages centralization of behavior, which can aid a developer in implementing a course of fallback.Hope that helps.

  • Brian O’Connell

    One of the objectives of unobtrusive scripting is the seperation of behaviour from the presentation. Another objective is progressive enhancement/graceful degredation. You don’t do one and simply accept the other as a benefit. It’s a package deal and either or both could be reasons for adopting the practice.

    When I mentioned the essence of unobtrusive scripting to me I suppose I meant the GD is what appeals to me and I have a particular desire to have a webpage that works with or without script. It’s often a requirement as I develop for Local Government. That makes GD a much stronger reason to be doing these things from my point of view.

    Anyway thanks for clearing things up on the knockout front. It makes what I’m saying a bit irrelevant.

  • http://www.userinexperience.com Brandon Satrom

    Brian,

    Not irrelevant at all. Perhaps tangential, but considering that you aren’t familiar with Knockout, it makes sense that you would ask, and I’m glad you did.

    As far as UJS goes, I think we agree in spirit, if not in the particulars. In any case, since UJS doesn’t have a categorical definition, we’re probably both right enough, given the context we each bring to the definition.

  • http://blueonionsoftware.com/blog.aspx?p=db917bb8-a7cf-40a3-9326-8e9f4a090410 Friday Links #166 | Blue Onion Software *

    [...] Introducing the Knockout.Unobtrusive Plugin | User InExperience Announcing YUI 3.4.0 and the new YUILibrary.com » Yahoo! User Interface Blog (YUIBlog) spin.js A Really Nice Way To Handle Popup Information Top 10 Mobile Web Development JavaScript Frameworks Barrier in .NET 4.0 – Daily .Net Tips Stephen Forte`s Blog – A New Era in Application Development, Part II: Kendo UI Persistent Headers ThreadPool vs. Tasks – Paul Stovell Welcome to Building Windows 8 – Building Windows 8 – Site Home – MSDN Blogs [...]

  • http://csell.net/2011/08/24/a-few-beers-and-javascript-with-brandon-satrom/ A few beers and JavaScript with Brandon Satrom » Clark Sell

    [...] Knockout.Unobtrusive [...]

Switch to our mobile site