Angular Light
Two-way databinding for model-view using a very rough design based on Angular 1. Rather than creating (elements * binds = watchers) we create a master event watcher for each controller/view that listens to all the DOM elements. This reduces the number of binds a LOT.
by Xeoncross
HTML
<h1>Site Title</h1>
<!-- We could later add the <ui-view> / Router thing here -->
<div id="main" data-controller="FooCtrl"></div>
<!-- template load by AJAX or embedded down here -->
<template id=t>
<div>
<!--
Neat data-binding example
-->
<form>
<h3>Model Value: {{ boundvalue }}</h3>
Change it here:<input value="{{ boundvalue }}" data-model="boundvalue" data-event="change" />
</form>
<!--
Other straight template-y stuff
-->
<p><a href="{{ url }}">{{ foo.bar.baz }} {{ quux }} </a>!</p>
<!--
Loop over arrays. Use data attributes of
data-repeat-[name]="{{ x.y.z }}" and reference
the values iterated over using {{ [name] }}.
For arrays the 'i' state variable is also set.
-->
<ul data-repeat-num="{{ list }}">
<li>{{ num }} {{ i }}</li>
</ul>
<!--
Loop over Objects. For objects the 'key' state variable is also set.
-->
<ul data-repeat-o="{{ anobj }}">
<li>{{ key }}={{ o.name }}</li>
</ul>
</div>
</template>
CSS
/*
https://gist.github.com/Xeoncross/da4a9397d6ae5c936085
Two-way databinding for model-view using a very rough design based on Angular 1. Rather than creating (elements * binds = watchers) we create a master event watcher for each controller/view that listens to all the DOM elements. This reduces the number of binds a LOT.
DOM-diffs, DI, $scope digests, Router, etc.. are all missing from this demo. Anything not related to binding to the DOM<->Model is missing.
Pay attention to the `{{ boundvalue}}` in the this.data and HTML on the left. It is updated based on the `data-event="change"` which tells the controller this element CARES about the change event.
*/
#main {
border: 1px solid #ddd;
padding: 1em;
}
JavaScript
// https://gist.github.com/jcgregorio/7fa68cdced1181416559
(function(ns) {
var root = ns;
var re = /{{\s([\w\.]+)\s}}/;
function updateState(obj, is, value) {
if (typeof is == 'string')
return updateState(obj,is.split('.'), value);
else if (is.length==1 && value!==undefined)
return obj[is[0]] = value;
else if (is.length==0)
return obj;
else
return updateState(obj[is[0]],is.slice(1), value);
}
function filterState(address, state) {
var mystate = state;
address.forEach(function(a) {
if (mystate.hasOwnProperty(a)) {
mystate = mystate[a];
} else {
throw a + " is not a valid property of " + JSON.stringify(mystate);
}
});
return mystate;
}
function ssplice(str, index, count, add) {
return str.slice(0, index) + add + str.slice(index + count);
}
function addressOf(s) {
if ((match = re.exec(s)) != null) {
return match[1].split(".");
} else {
return null;
}
}
function expandString(s, state) {
var match;
var found = false;
while ((match = re.exec(s)) != null) {
found = true;
address = match[1].split(".");
m = filterState(address, state);
s = ssplice(s, match.index, match[0].length, m);
}
if (found) {
return s;
}
return null;
}
function expand(e, state) {
if (e.nodeName == "#text") {
m = expandString(e.textContent, state);
if (m != null) {
e.textContent = m;
}
}
if (e.attributes != undefined) {
for (var i=0; i<e.attributes.length; i++) {
var attr = e.attributes[i];
if (attr.name.indexOf('data-repeat') === 0) {
var parts = attr.name.split('-');
if (parts.length != 3) {
throw "Repeat format is data-repeat-[name]. Got " +...