It’s with this in mind that I dug into Knockout. I started with an existing app, and added Knockout to a User Profile page that provides the user with a real-time view of what their public profile will look like on the site, as they make changes to the details of that profile.
Here’s a simplified version of my ViewModel, Knockout-style:
And a simplified version of my markup.
And that all works great. I have a nice separation, but I found myself wondering if the separation could be a bit cleaner. My primary concern was around Knockout’s method for binding, the data-bind attributes on each element.
Take this line from my addSpeaker page, for example:
I can’t help but feel that this line is expressing more than metadata. It’s expressing behavior. Two separate behaviors, in fact. First it provides a boolean expression that determines if the button should be enabled (“enable: languageToAdd().length > 0”). Then it specifies a click handler for that button (“click: addLanguage”).
I might be totally off base here, but the latter feels not that far removed from this:
Now I’m not dismissing Knockout here, or any other MVVM framework, for that matter. These were all created by chaps far, far smarter than I am. For my use, though, I wanted to see if taking the data-* bindings out of my markup all together would result in a structure even cleaner and more maintainable. The rest of this post is the result of a couple hours of work in that direction.
I started by creating an object to hold all of my bindings for the page:
My bindings object allows me to specify the properties bound to text fields (of the same Id, by my convention), those bound to the options property of a <select>, and a set of custom bindings, where I can specify anything Knockout will support.
After I create this object, and just before I call ko.applyBindings(viewModel)—the line that tells Knockout to do it’s wondrous magic—I call this line:
modelBinder and the createBindings function, were created to take my bindings object and do all of the data-* goodness for me. It looks like this:
For my inputs and options sub-collections, I’m using some additional conventions I defined, mainly that the bind string is “value: “ + name for inputs and “options: “ + name for my array items. I could have achieved the same result with just a big list of custom bindings and a shorter createBindings() method, but doing this make the binding object cleaner and more maintainable, which I felt was worth the tradeoff.
After those additions, I swept back through my markup and replaced the data-bind attributes with Id’s.
So what do you think? Is this cleaner, or do I need to have a stiff drink and accept data-* and Knockout for what it is? If you think the spirit is right, but the execution is wrong, I’d love to hear that too.
Here’s the gist for my initial code and markup, and the end-result, after my tomfoolery. If you want to play with this example yourself, I’ve created a Fiddle with a working sample that you can check out here.
- No public Twitter messages.