Skip to content

Instantly share code, notes, and snippets.

@Akeri
Created June 1, 2020 16:41
Show Gist options
  • Save Akeri/6062ed70db12f4d612b3a41cf07403d9 to your computer and use it in GitHub Desktop.
Save Akeri/6062ed70db12f4d612b3a41cf07403d9 to your computer and use it in GitHub Desktop.
Simple jQuery template/data render
/**
* Class for single data binding management. Converts Markdown data into HTML data.
*
* @author Brais Saco Estévez
* @requires showdown
* @url https://github.com/showdownjs/showdown
*
* @lint global Binder
*/
window.Binder = class Binder {
/**
* Class constructor.
* @param {object} config Configuration object
*/
constructor(config) {
if (window.bindManager) return window.bindManager;
this.bindSelector = config.bindSelector || '[data-bind]';
this.bindLoopSelector = config.bindLoopSelector || '[data-bind-loop]';
this.bindTemplateRegExp = config.bindTemplateRegExp || /{{[\s]*(.+?)[\s]*}}/ig;
this.showdown = new window.showdown.Converter(config.showdown);
}
/**
* Bind data to actual HTML view.
* @param {Object} data Values to bind to HTML
* @param {string|jQuery} scope Main DOM wrapper to apply data binding
*/
apply(data, scope = 'body') {
this.restoreOriginalHtml(scope);
this.bindLoop(data, scope); // It may be the first.
this.bindTemplate(data, scope);
this.bindAttribute(data, scope);
}
/**
* Bind data to actual HTML view using template references.
* @param {Object} data Values to bind to HTML
* @param {string|jQuery} scope Main DOM wrapper to apply data binding
*/
bindTemplate(data, scope = 'body') {
if (!data) {
console.error("Binder => No data to bind");
return;
}
let self = this;
let processedHtml = $(scope)
.html()
.replace(self.bindTemplateRegExp, (matched, dataReference) => {
let content = self.searchInObject(dataReference, data);
let html = self.parseToHtml(content);
return html;
});
$(scope).html(processedHtml);
}
/**
* Bind data to actual HTML view using DOM element attribute binding
* @param {Object} data Values to bind to HTML
* @param {string|jQuery} scope Main DOM wrapper to apply data binding
*/
bindAttribute(data, scope = 'body') {
if (!data) {
console.error("Binder => No data to bind");
return;
}
let self = this;
$(scope).find(this.bindSelector).each((idx, elem) => {
let $elem = $(elem);
let bindRef = $elem.data('bind');
let bindProcess = $elem.data('data-bind-process') || false;
let content = self.searchInObject(bindRef, data);
if (!!content && !bindProcess) {
let html = self.parseToHtml(content);
$elem
.html(html)
.data('data-bind-process', true);
}
})
}
/**
* Create children for a DOM element using collection.
* @param {Object} data Values to bind to HTML
* @param {string|jQuery} scope Main DOM wrapper to apply data binding
*/
bindLoop(data, scope) {
if (!data) {
console.error("Binder => No data to bind");
return;
}
let self = this;
$(scope).find(this.bindLoopSelector).each((idx, elem) => {
let $elem = $(elem);
let elemInnerHtml = $elem.html();
let bindRef = $elem.data('bind-loop');
let bindProcess = $elem.data('data-bind-loop-process') || false;
let content = data;
if (bindRef) {
content = self.searchInObject(bindRef, data);
}
if (!!content && !bindProcess) {
$elem
.empty()
.data('data-bind-loop-process', true);
content.forEach((rowData) => {
let $innerContent = $(elemInnerHtml);
$elem.append($innerContent);
self.bindAttribute(rowData, $innerContent);
self.bindTemplate(rowData, $innerContent);
});
}
});
}
/**
* Retrieve object reference.
* @param {string} reference Point separated reference
* @param {Object} dataObject Object to find in.
* @returns {object|string} Referenced value in object
*/
searchInObject(reference, dataObject) {
try {
return reference.split('.').reduce((a, v) => a[v], dataObject);
} catch (error) {
console.warn("No reference found", reference, dataObject);
return '[' + reference + ' : N/A]';
}
}
/**
* Converts markdown to HTML.
* @param {string} contents Markdown contents
* @returns {string} HTML contents
*/
parseToHtml(contents) {
// Convert markdown to HTML
let html = this.showdown.makeHtml(contents);
// Unwrap single <p> elements.
let $wrapper = $("<div></div>").html($(html));
if ($wrapper.children().length === 1) {
html = $(html).unwrap('p').html();
}
return html;
}
/**
* Store original HTML contents into DOM object data.
* @param {string|jQuery} scope DOM element
*/
storeOriginalHtml(scope) {
let originalHtml = $(scope).html();
$(scope).data('bind-original', originalHtml);
}
/**
* Restore original HTML contents from DOM object data or stores it if doesn't exists yet.
* @param {string|jQuery} scope DOM element
*/
restoreOriginalHtml(scope) {
let originalHtml = $(scope).data('bind-original');
if (!originalHtml) {
this.storeOriginalHtml(scope);
} else {
$(scope).html(originalHtml);
}
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment