Last active
October 7, 2015 05:27
-
-
Save domenic/3112701 to your computer and use it in GitHub Desktop.
Proof of concept of ES5 data binding
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"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; | |
}()); |
@rwldrn: Object.keys(defaults).forEach(function (propName) {
starts a new dynamic scope.
@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
Why are you using
var that = this;
inES5VM
(starting at line 45)? There is nothing preventing reference of the actualthis
.