This is not an introduction to Knockout or jQuery, the Internet is replete with fine examples and tutorials. For me, these two tools are key components in modern web development, and it would seem Microsoft agrees since they are both installed by default with the new MVC4 templates:
To highlight the issue, I’m going to share with you something that happened to me recently. I was working on an application that uses Twitter Bootstrap (see, there’s yet another library at play) for the UI. The app makes heavy use of Knockout for data binding and jQuery for eventing. Here is some psuedo-code to outline the problem:
<div class="row-fluid" data-bind="if: Companies() !== undefined && Companies().length > 0"> <!-- lots of Bootstrap stuff cut out for brevity --> <ul class="nav nav-pills"> <li id="option1"><a>option 1</a></li> <li id="option2"><a>option 2</a></li> <li id="option3"><a>option 3</a></li> </ul> <div class="content"> <!-- show content here --> </div> </div>
The problem is that the click event handlers for the option1 and option2 nav pills never fire. I stared at this for quite awhile, made a lot of incorrect guesses, and tried a lot of solutions, all to no avail. The frustration was exacerbated because at one point they DID work. Turns out a seemingly unrelated change was implemented and the UI was never retested, but I’ll address that shortly. Finally, I *saw* it and all was clear.
If you are an experienced Knockout developer, you may have already spotted the culprit. If not don’t worry, I won’t make you wait: the offense here is in using the Knockout ‘if’ binding. On the surface, the if binding acts like the visibility binding, only showing the HTML element (and hence its children) if the matching condition is true. What if actually does is physically add and remove elements from the DOM, which is what caused the problem.
When the page loads, and the bindings are created, the value of Companies() is an empty observableArray, which caused the if binding to interpret as false and remove the DOM elements. Since the elements are no longer part of the DOM, jQuery had no targets to bind the events. In other words, it was functioning exactly as designed.
It turns out I had recently moved this particular if binding higher in the DOM heirarchy. It used to be in the div after the nav-pills and moving it back there fixed the bug. Another solution would have been to change the binding from if to visible. I’m sure there are more complex solutions, like dynamically adding the jQuery bindings but that seemed unnecessary and nontrivial.
At the end of the day, it was not Bootstrap, Knockout or jQuery at fault: it was me. The true cause was my choice of if instead of visible because I liked the syntax better and not for any technical reason.
What really makes it worse is that by the time I moved the code that caused the problem to surface I DID know how the if binding functioned. I failed to see the unintended consequence and worse still I failed to test the app after the change. In other words, I failed KWHYD at several times during the process. The good news is I won’t make that mistake with if again…