Skip to content

Instantly share code, notes, and snippets.

@domenic
Last active October 7, 2015 05:27
Show Gist options
  • Save domenic/3112701 to your computer and use it in GitHub Desktop.
Save domenic/3112701 to your computer and use it in GitHub Desktop.
Proof of concept of ES5 data binding
<!DOCTYPE html>
<html>
<head>
<title>ES5 Data Binding Proof of Concept</title>
</head>
<body>
<section>
<label>
Name:
<input data-bind="name" type="text" />
</label>
</section>
<hr />
<section>
<button id="changeName">Change name</button>
<pre>
viewModel.name = window.prompt("Enter the new name");
</pre>
</section>
<section>
<button id="alertName">Alert name value from view model</button>
<pre>
alert(viewModel.name);
</pre>
</section>
<script src="es5vm.js"></script>
<script>
"use strict";
(function () {
var viewModel = new ES5VM({
name: ""
});
ES5VM.bindTogether(document.body, viewModel);
function changeName() {
viewModel.name = window.prompt("Enter the new name");
}
function alertName() {
alert(viewModel.name);
}
document.getElementById("changeName").addEventListener("click", changeName);
document.getElementById("alertName").addEventListener("click", alertName);
}());
</script>
</body>
</html>
"use strict";
window.ES5VM = (function () {
// This is just implemented for inputs (i.e. things with `value` properties and `input` events),
// but obviously you could create different types and use the correct one based on the tag name of
// the element you bind to.
function Binding() {
var boundEl = null;
var value = undefined;
function getValueFromEl() {
return boundEl.value;
}
function updateEl() {
boundEl.value = value;
}
this.get = function () {
if (value !== undefined) {
return value;
}
return boundEl ? getValueFromEl() : undefined;
};
this.set = function (newValue) {
value = newValue;
if (boundEl) {
updateEl();
}
};
this.bindTo = function (el) {
boundEl = el;
boundEl.addEventListener("input", function () {
value = getValueFromEl();
});
if (value !== undefined) {
updateEl();
}
};
Object.freeze(this);
}
function ES5VM(defaults) {
var that = this;
// Ugh fake private variables :(
Object.defineProperty(that, "_bindings", { value: Object.create(null) });
Object.keys(defaults).forEach(function (propName) {
that._bindings[propName] = new Binding();
Object.defineProperty(that, propName, {
get: function () {
return that._bindings[propName].get();
},
set: function (value) {
return that._bindings[propName].set(value);
},
configurable: true,
enumerable: true
});
that[propName] = defaults[propName];
});
// We may not be able to make it private but we're going to try damn hard not to let them mess it up.
Object.freeze(that._bindings);
// Don't let people try to add new properties to the view models; they must have fixed shape.
// Otherwise they might be under the impression that they'll correctly data-bind, which we can't do.
Object.preventExtensions(that);
}
ES5VM.bindTogether = function (element, viewModel) {
var boundEls = element.querySelectorAll("[data-bind]"));
Array.prototype.forEach.call(boundEls, function (el) {
var propName = el.getAttribute("data-bind");
viewModel._bindings[propName].bindTo(el);
});
};
return ES5VM;
}());
@Couto
Copy link

Couto commented Sep 12, 2013

@domenic I might be wrong here, but Array.prototype.forEach accepts two arguments, being the second argument the thisArg which would solve the var that = this;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment