-
Star
(110)
You must be signed in to star a gist -
Fork
(15)
You must be signed in to fork a gist
-
-
Save jcgregorio/7fa68cdced1181416559 to your computer and use it in GitHub Desktop.
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title></title> | |
| <meta charset="utf-8" /> | |
| <script src="templating.js" type="text/javascript" charset="utf-8"></script> | |
| </head> | |
| <body> | |
| <template id=t> | |
| <div> | |
| <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> | |
| <script type="text/javascript" charset="utf-8"> | |
| var clone = document.importNode(document.querySelector('#t').content, true); | |
| data = { | |
| foo: { bar: { baz: "Hello"}}, | |
| quux: "World", | |
| list: ["a", "b", "c"], | |
| anobj: { | |
| foo: {name: "Fred"}, | |
| bar: {name: "Barney"} | |
| }, | |
| url: "http://example.com", | |
| }; | |
| Expand(clone, data); | |
| document.body.appendChild(clone); | |
| </script> | |
| </body> | |
| </html> |
| // Copyright (c) 2014 Google Inc. All rights reserved. | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are | |
| // met: | |
| // | |
| // * Redistributions of source code must retain the above copyright | |
| // notice, this list of conditions and the following disclaimer. | |
| // * Redistributions in binary form must reproduce the above | |
| // copyright notice, this list of conditions and the following disclaimer | |
| // in the documentation and/or other materials provided with the | |
| // distribution. | |
| // * Neither the name of Google Inc. nor the names of its | |
| // contributors may be used to endorse or promote products derived from | |
| // this software without specific prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| (function(ns) { | |
| var root = ns; | |
| var re = /{{\s([\w\.]+)\s}}/; | |
| 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 " + attr.name; | |
| } | |
| var name = parts[2]; | |
| var tpl = e.removeChild(e.firstElementChild); | |
| var address = addressOf(attr.value); | |
| if (address == null) { | |
| throw attr.value + " doesn't contain an address."; | |
| } | |
| var childState = filterState(address, state); | |
| if ('forEach' in childState) { | |
| childState.forEach(function(item, i) { | |
| var cl = tpl.cloneNode(true); | |
| var instanceState = {}; | |
| instanceState[name] = item; | |
| instanceState["i"] = i; | |
| expand(cl, instanceState); | |
| e.appendChild(cl); | |
| }); | |
| } else { | |
| Object.keys(childState).forEach(function(key) { | |
| var cl = tpl.cloneNode(true); | |
| var instanceState = {}; | |
| instanceState[name] = childState[key]; | |
| instanceState["key"] = key; | |
| expand(cl, instanceState); | |
| e.appendChild(cl); | |
| }); | |
| } | |
| } else { | |
| m = expandString(attr.value, state); | |
| if (m != null) { | |
| e[attr.name] = m; | |
| } | |
| } | |
| } | |
| } | |
| for (var i=0; i<e.childNodes.length; i++) { | |
| expand(e.childNodes[i], state); | |
| } | |
| } | |
| root.Expand = expand; | |
| })(this); |
Wouldn't be cleaner change the i and key state variables for repeat_index and repeat_key? It's more readable and reduce the posibility of variables conflict, and also other template mechanism Templator do the same.
trying to see if you can nest data-repeat-[keys] it seems not possible with this iteration, anyone plan on implementing it?
A use case would be looping over an array of objects.
Kudos, this is great @jcgregorio. And nice work, @xeoncross expanding upon it.
I think the cleaned up version of this code, which now lives in https://github.com/jcgregorio/stamp/ addresses the issues you've raised:
@piranna The i and key state variable names can now optionally be set by appending a -name to the data attribute.
@gabrielcsapo Nesting now works for data-repeat.
@jcgregorio I added the most basic data-binding and controller support to expand this example to be a mini-angular of sorts... sort-of. Change the text input and watch as the model updates and then the controller re-complies the template and updates the DOM.
http://jsfiddle.net/Xeoncross/jtk10tL5/