It is rare that a day passes on the Western Devs' slack channel that we don't have some lively discussion. Today was my day to rant about knockout.js.
If you haven't used knockout.js it is a JavaScript library used for bi-directional model binding. It was the first library of its type that I encountered, being deeply involved in the Microsoft web mentality at that point.
My current project uses knockout.js and although we've been successful with it there has been a great deal of pain. In fact it has been so irritating that I no longer recommend using knockout to anybody working on a serious project. This may seem crazy because there are a lot of sites out there, big sites, that make use of knockout. The new Azure portal is, perhaps, the most famous of these sites.
What are my issues? Allow me to tell you:
- The ko.observable model creates confusion between what is an observable and what isn't. There is not a day that passes that I don't mess up a binding or a piece of logic because I expect a variable to be a value and it is a function. This is because binding requires using ko.observables to actually function properly. I don't know if it is helpful that binding in either way works for basic properties.
1 | <span data-bind="someproperty"/> |
1 | <span data-bind="someproperty() + 1"/> |
- Observable array is missing a bunch of functions that exist on Array. I'm not talking just the new array prototype functions in ES6 but older functions like filter which have been around since 2011. This is super confusing. Why aren't the rest of the functions implemented? I'm not sure. I find the lack of filter especially irritating.
- The syntax around loops is messy. If you're in a foreach loop then it is really weird to get stuff from the outside
1 | <tbody data-bind="foreach: somecollection"> |
- Binding using data- attributes encourages you to move your logic into the html code where it is all but untestable
1 | <select class="form-control" data-bind="value: item.State, |
-
Knockout supports creating components but these are rendered at a non-deterministic time. This means that if you want to do something after the component has rendered you're pretty much left guessing. You can't do it right after you call the model bind because that is defered and as there is no componentHasMounted equivilent to hook into you can't do it there. If you wanted to, say, add an autocomplete to a dynamically added text box then when would you call that code?
I honestly don't know. I've tried just setting a timeout and I've also tried hooking into mutation observers. The first is hacky and the second has limited browser support.
Update: Dave and I poked about a bit and there might be a solution in using synchronous components, I'll experiment and update some more.