Refactor data-binding to own class

This commit is contained in:
Jeremy Likness 2019-11-26 09:15:12 -08:00
parent 0cab33efe5
commit 47a3dc8ba5
2 changed files with 60 additions and 32 deletions

50
js/dataBinding.js Normal file
View File

@ -0,0 +1,50 @@
// @ts-check
export class DataBinding {
/**
* Simple evaluation
* @param {string} js The JavaScript to evaluate
*/
execute(js) {
return eval(js);
}
/**
* Evaluates JavaScript with a constrained context (scope)
* @param {string} src The JavaScript to evaluate
* @param {object} context The context (data) to evaluate with
* @returns {object} The result of the evaluation
*/
executeInContext(src, context) {
return this.execute.call(context, src);
}
/**
* Searches for "repeat" attribute to data-bind lists
* @param {HTMLElement} elem The parent element to search
* @param {object} context The context to use for binding
*/
bindLists(elem, context) {
const listBinding = elem.querySelectorAll("[repeat]");
listBinding.forEach(elem => {
const parent = elem.parentElement;
const expression = elem.getAttribute("repeat");
elem.removeAttribute("repeat");
const template = elem.outerHTML;
parent.removeChild(elem);
context[expression].forEach(item => {
let newTemplate = `${template}`;
const matches = newTemplate.match(/\{\{([^\}]*?)\}\}/g);
if (matches) {
matches.forEach(match => {
match = match.replace("{{", "").replace("}}", "");
const value = this.executeInContext(`this.${match}`, {item});
newTemplate = newTemplate.replace(`{{${match}}}`, value);
});
parent.innerHTML += newTemplate;
}
});
});
}
}

View File

@ -1,4 +1,7 @@
// @ts-check
import {DataBinding} from "./dataBinding.js"
/**
* Represents a slide
* */
@ -19,6 +22,11 @@ export class Slide {
* @type {object}
*/
this._context = {};
/**
* Data binding helper
* @type {DataBinding}
*/
this._dataBinding = new DataBinding();
/**
* The HTML DOM hosting the slide contents
* @type {HTMLDivElement}
@ -57,41 +65,11 @@ export class Slide {
// execute any scripts
const script = this._html.querySelector("script");
if (script) {
(function (/** @type {string} */src) {
return eval(src)
}).call(this._context, script.innerText);
this.dataBind();
this._dataBinding.executeInContext(script.innerText, this._context);
this._dataBinding.bindLists(this._html, this._context);
}
}
/**
* Scans for data-binding and applies the bindings
*/
dataBind() {
const listBinding = this._html.querySelectorAll("[repeat]");
listBinding.forEach(elem => {
const parent = elem.parentElement;
const expression = elem.getAttribute("repeat");
elem.removeAttribute("repeat");
const template = elem.outerHTML;
parent.removeChild(elem);
this._context[expression].forEach(item => {
let newTemplate = `${template}`;
const matches = newTemplate.match(/\{\{([^\}]*?)\}\}/g);
if (matches) {
matches.forEach(match => {
match = match.replace("{{", "").replace("}}", "");
const value = (function (/** @type {string} */src) {
return eval(src)
}).call({ item }, match);
newTemplate = newTemplate.replace(`{{${match}}}`, value);
});
parent.innerHTML += newTemplate;
}
});
});
}
/**
* The slide transition
* @returns {string} The transition name