Last active
August 16, 2018 13:29
-
-
Save ronen-e/32bbdfd9f7799536938147d3bbba15a0 to your computer and use it in GitHub Desktop.
Table render exercise with pure javascript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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