Skip to content

Instantly share code, notes, and snippets.

@domenic
Created February 10, 2012 21:51
Show Gist options
  • Save domenic/1793211 to your computer and use it in GitHub Desktop.
Save domenic/1793211 to your computer and use it in GitHub Desktop.
Speculations on an ES5-style KnockoutJS
// DESIRED (pending bikeshedding):
// Create using special factory function. Will automatically create `ko.observable`s, `ko.observableArray`s, and
// `ko.computed`s for you, but hide them behind getters/setters.
var viewModel = es5ViewModel({
firstName: "Luke",
lastName: "Skywalker",
fullName: function () {
return this.fullName + this.lastName;
},
parents: ["Anakin", "Padme"]
});
// If you want commands, attach them manually: functions passed to `es5ViewModel` will become `ko.computed`s.
viewModel.drawLightsaber = function () { };
// `applyES5Bindings` is like `ko.applyBindings`, but takes ES5 view models instead of Knockout view models.
applyES5Bindings(viewModel, document.getElementById("jedi-info"));
// You can set and get properties without the annoying pseudo-getter/setter syntax of observables.
viewModel.firstName = "Luuke";
console.log(viewModel.lastName); // Skywalker
console.log(viewModel.parents); // Anakin, Padme
viewModel.parents = ["Darth Vader", "Queen Amidala"];
console.log(viewModel.fullName); // Luuke Skywalker
// Computed observables don't have setters. (I know Knockout has the ability to make this happen, but I think YAGNI).
// -----------
// `applyES5Bindings` is the only sticky point: we need to get a Knockout view model from the ES5 one.
// SOLUTION 1
// Would look nicer with ES Harmony destructuring assignment: replace first three lines with
// `let [viewModel, koBindings] = es5ViewModel({});`
var x = es5ViewModel({});
var viewModel = x.viewModel;
var koBindings = x.koBindings;
ko.applyBindings(koBindings, document.getElementById("jedi-info"));
// SOLUTION 1.5
// If we don't need to separate creation from binding, then this could work:
var viewModel = boundES5ViewModel(document.getElementById("jedi-info"), {});
// Not sure that's the case very often though.
// SOLUTION 2
// Use a map of ES5 VMs to KO VMs. `applyES5Bindings` would do a lookup.
// Really only a responsible thing to do with a ES Harmony WeakMap available; otherwise not very memory-conscious.
// ES Harmony private names would also do the trick.
// SOLUTION 3:
var viewModel = es5ViewModel({});
ko.applyBindings(viewModel._koBindings, document.getElementById("jedi-info"));
// (could encapsulate access to `_koBindings` inside `applyES5Bindings`.)
// SOLUTION 4:
// Even more underscores. Expose a _'ed counterpart to all original properties that gives direct access
// to the Knockout observable. `applyES5Bindings` would then reconstruct a Knockout view model from those.
// CONCLUSION
// I think I like solution 3 the most. Normally I would loathe the lack of encapsulation, but view models shouldn't be
// used by anyone except rendering code, so nobody else should have to deal with this wart.
//----------
// COMPLICATED PARTS:
// * Nested ES5 view models/ES5 view models contained in arrays. *Shudder*.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment