Skip to content

Instantly share code, notes, and snippets.

@ronen-e
Last active August 16, 2018 13:29
Show Gist options
  • Save ronen-e/32bbdfd9f7799536938147d3bbba15a0 to your computer and use it in GitHub Desktop.
Save ronen-e/32bbdfd9f7799536938147d3bbba15a0 to your computer and use it in GitHub Desktop.
Table render exercise with pure javascript
(function(window){
// sample data from server
var data1 = [ { "company_name":"Medline Industries, Inc.", "product":"Benzalkonium Chloride", "price":"481.63" }, { "company_name":"PD-Rx Pharmaceuticals, Inc.", "product":"Alprazolam", "price":"167.62", "fda_date_approved":"02/12/2015" }, { "company_name":"West-ward Pharmaceutical Corp.", "product":"Flumazenil", "fda_date_approved":"23/04/2015" }, { "company_name":"HyVee Inc", "product":"Aspirin", "price":"218.32", "fda_date_approved":"26/07/2015" }, { "company_name":"Aurobindo Pharma Limited", "product":"carisoprodol", "price":"375.58", "fda_date_approved":"28/11/2014" }, { "company_name":"Apotex Corp", "product":"Risperidone", "price":"213.49", "fda_date_approved":"06/11/2015" }, { "company_name":"Unit Dose Services", "product":"Lovastatin", "price":"169.14", "fda_date_approved":"14/09/2015" }, { "company_name":"Jubilant HollisterStier LLC", "product":"Dog Hair Canis spp.", "fda_date_approved":"31/12/2014" }, { "company_name":"AAA Pharmaceutical, Inc.", "product":"ACETAMINOPHEN, CHLORPHENIRAMINE MALEATE, DEXTROMETHORPHAN HYDROBROMIDE, and PHENYLEPHRINE HYDROCHLORIDE", "price":"183.33", "fda_date_approved":"13/12/2015" }, { "company_name":"AKG Innovations LLC", "product":"AVOBENZONE, OCTINOXATE, OCTISALATE", "fda_date_approved":"22/01/2015" }, { "company_name":"hikma Farmaceutica", "product":"Oxytocin" }, { "company_name":"prime Packaging, Inc.", "product":"Avobenzone, Homosalate, Octisalate, Octocrylene, Oxybenzone", "price":"208.17" }, { "company_name":"Davion, Inc", "product":"Triclosan", "price":"80.30", "fda_date_approved":"13/12/2014" }, { "company_name":"CARDINAL HEALTH", "product":"CARBOXYMETHYLCELLULOSE SODIUM, GLYCERIN", "price":"330.22", "fda_date_approved":"11/08/2015" }, { "company_name":"Amgen Inc", "product":"darbepoetin alfa", "price":"332.28", "fda_date_approved":"01/07/2015" }, { "company_name":"Autumn Harp, Inc.", "product":"Salicylic Acid", "price":"34.43", "fda_date_approved":"25/03/2015" }, { "company_name":"American Regent, Inc.", "product":"sodium phosphate, monobasic, monohydrate and sodium phosphate, dibasic anhydrous", "price":"11.60" }, { "company_name":"J. A. Cosmetics U.S. INC", "product":"TITANIUM DIOXIDE", "price":"130.90", "fda_date_approved":"01/12/2015" }, { "company_name":"NATURE REPUBLIC CO., LTD.", "product":"Titanium Dioxide, OCTINOXATE, Zinc Oxide", "price":"124.48" }, { "company_name":"L. Perrigo Company", "product":"Dextromethorphan Hydrobromide, Guaifenesin", "price":"73.09", "fda_date_approved":"03/02/2016" } ];
var data2 = [ { "first_name": "Billy", "last_name": "Campbell", "phone": "62-(500)527-5325" }, { "first_name": "Jonathan", "last_name": "Black", "country": "Russia", "phone": "7-(729)811-4597" }, { "first_name": "cheryl", "last_name": "Harvey", "country": "Indonesia", "phone": "62-(825)454-3810" }, { "first_name": "Cynthia", "last_name": "Cooper" }, { "first_name": "Thomas", "last_name": "Stevens", "phone": "86-(527)535-8464" }, { "first_name": "Jane", "last_name": "Chavez", "country": "Netherlands" }, { "first_name": "bobby", "last_name": "Price", "country": "China", "phone": "86-(898)723-6749" }, { "first_name": "Steve", "last_name": "Hansen", "phone": "93-(362)494-5552" }, { "first_name": "Alan", "last_name": "Cruz", "country": "Philippines", "phone": "63-(617)248-8832" }, { "first_name": "Dennis", "last_name": "Baker", "country": "Iran", "phone": "98-(436)329-3723" }, { "first_name": "Ernest", "last_name": "Bishop", "phone": "86-(566)429-1138" }, { "first_name": "Russell", "last_name": "Meyer", "phone": "62-(687)827-4302" }, { "first_name": "Ryan", "last_name": "Mendoza", "country": "Poland", "phone": "48-(537)109-0373" }, { "first_name": "Maria", "last_name": "Greene", "phone": "92-(831)367-8049" }, { "first_name": "Elizabeth", "last_name": "Moore", "country": "Philippines", "phone": "63-(694)844-9255" }, { "first_name": "Ronald", "last_name": "kim", "phone": "46-(339)931-9221" }, { "first_name": "Samuel", "last_name": "Jacobs", "country": "Russia", "phone": "7-(936)156-5229" }, { "first_name": "Fred", "last_name": "Ross", "phone": "55-(594)481-7354" }, { "first_name": "Andrew", "last_name": "Burns", "country": "Portugal", "phone": "351-(174)443-8706" }, { "first_name": "Robert", "last_name": "Frazier", "country": "Somalia" } ];
function main() {
octopus.init(data1);
view.render();
}
var ASC = 'ASC';
var DESC = 'DESC';
var model = {
sortKey: null,
sortDirection: DESC,
data: null,
headers: null
};
var view = {
templates: {
table: null,
tbody: null
},
init: function init() {
// set root element and templates
this.el = document.getElementById('root');
var tableTpl = document.getElementById('tpl').innerHTML;
this.templates.table = template(tableTpl);
var tbodyTpl = document.getElementById('tpl-body').innerHTML;
this.templates.tbody = template(tbodyTpl);
},
componentDidMount: function componentDidMount() {
var clickHandler = this.clickHandler.bind(this);
this.el.querySelectorAll('th')
.forEach(function addClickHandler(th) {
th.addEventListener('click', clickHandler);
});
},
clickHandler: function clickHandler(event) {
var templateData = octopus.getTemplateData(event.target.getAttribute('data-sort-key'));
this.renderTableBody(templateData);
},
renderTableBody: function renderTableBody(templateData) {
this.el.querySelector('tbody').innerHTML = this.templates.tbody(templateData);
},
render: function render() {
this.el.innerHTML = this.templates.table(octopus.getTemplateData());
this.componentDidMount();
}
};
var octopus = {
init: function init(appData) {
model.data = appData;
view.init();
},
getHeaders: getHeaders,
getTemplateData: function getTemplateData(sortKey) {
var data = model.data.slice();
if (sortKey) {
data = this.sortBy(data, sortKey);
}
return {
data: data,
headers: this.getHeaders(model.data)
};
},
sortBy: function sortBy(data, sortKey) {
// sortKey was changed
if (sortKey !== model.sortKey) {
model.sortKey = sortKey;
model.sortDirection = DESC;
} else {
// reverse sort directions
model.sortDirection = (model.sortDirection === ASC) ? DESC : ASC;
}
data.sort(function sort(a, b) {
return comparator(a[sortKey], b[sortKey], model.sortDirection);
});
return data;
}
};
// HELPERS
function getHeaders(data) {
if (!model.headers) {
var headers = new Set();
// iterate over data
data.forEach(function(item) {
Object.keys(item).forEach(function add(header) {
headers.add(header);
});
});
model.headers = Array.from(headers);
}
return model.headers;
}
// compare function - for sort
function comparator(a, b, dir) {
var result;
// protect against undefined values
if (isUndefined(a) || isUndefined(b)) {
return isUndefined(a) ? 1 : -1;
}
if (isNumber(a)) {
result = a - b;
} else if (isDate(a)) {
result = toDate(a) - toDate(b);
} else {
result = a.localeCompare(b);
}
return (dir === DESC) ? -(result) : result;
}
// inspired by John Resig's Micro templating http://ejohn.org/blog/javascript-micro-templating/
function template(str) {
str = str.replace(/<%~(.+)~%>/g, function insertChildTemplate(match, id) {
return document.getElementById(id.trim()).innerHTML;
});
// generate a new function as a template generator
var fn = new Function("params",
"var p = [];" +
"p.push('" +
str
.replace(/[\r\t\n]/g, " ") // reset \r\t\n
.split("<%").join("\t") // BEGIN expression
.replace(/((^|%>)[^\t]*)'/g, function(match){ return match.replace(/\'/g, "\r");}) // mark single quotes
.replace(/\t=(.*?)%>/g, "',$1,'") // expression value
.split("\t").join("');") // execute expression
.split("%>").join("p.push('") // END expression
.split("\r").join("\\'") // escape single quotes
+ "');return p.join('');");
return fn;
}
function isUndefined(v) {
return typeof v === 'undefined';
}
function isNumber(n) {
n = Number(n);
return typeof n === 'number' && !isNaN(n);
}
function isDate(d) {
return isDate.pattern.test(d);
}
isDate.pattern = /^(\d{2})\/(\d{2})\/(\d{4})$/;
function toDate(d) {
d = d.replace(isDate.pattern, "$2/$1/$3");
return new Date(d);
}
// namespace
var APP = {
main: main
};
window.APP = APP;
})(window);
APP.main();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Render Table</title>
<script id="tpl-body" type="text/template">
<% for (var i = 0, n = params.data.length; i < n; i++) { %>
<tr>
<% for (var j = 0, len = params.headers.length; j < len; j++) { var key = params.headers[j]; %>
<td><%= params.data[i][key] %></td>
<% } %>
</tr>
<% } %>
</script>
<script id="tpl" type="text/template">
<table id="table" border="1" cellpadding="3">
<thead>
<tr>
<% for (var i = 0, len = params.headers.length; i < len; i++) { %>
<th data-sort-key="<%= params.headers[i] %>"><%= params.headers[i] %></th>
<% } %>
</tr>
</thead>
<tbody id="tbody">
<%~ tpl-body ~%>
</tbody>
</table>
</script>
<style type="text/css">
th {
cursor: pointer;
}
</style>
</head>
<body>
<div id="root"></div>
<script src="solution.js"></script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment