0

One of the common patterns I've come across in my many years of coding is the structuring/binding of the data coming from the server response (XMLHttpRequest). This problem of creating elements and appending them in a particular order as well as binding (attributes,events,content) is what I'm am trying to achieve here.


For example purposes and simplicity I am trying to create a tr --- td nested structure as well as bind the attributes from the var instructs object (table-row,table-data).

JSON Response (dummy data)

var response =[{"employeeNumber":"1002","lastName":"Murphy","firstName":"Diane","extension":"x5800","email":"[email protected]","officeCode":"1","reportsTo":null,"jobTitle":"President"},{"employeeNumber":"1056","lastName":"Patterson","firstName":"Mary","extension":"x4611","email":"[email protected]","officeCode":"1","reportsTo":"1002","jobTitle":"VP Sales"},{"employeeNumber":"1076","lastName":"Firrelli","firstName":"Jeff","extension":"x9273","email":"[email protected]","officeCode":"1","reportsTo":"1002","jobTitle":"VP Marketing"},{"employeeNumber":"1088","lastName":"Patterson","firstName":"William","extension":"x4871","email":"[email protected]","officeCode":"6","reportsTo":"1056","jobTitle":"Sales Manager (APAC)"},{"employeeNumber":"1102","lastName":"Bondur","firstName":"Gerard","extension":"x5408","email":"[email protected]","officeCode":"4","reportsTo":"1056","jobTitle":"Sale Manager (EMEA)"},{"employeeNumber":"1143","lastName":"Bow","firstName":"Anthony","extension":"x5428","email":"[email protected]","officeCode":"1","reportsTo":"1056","jobTitle":"Sales Manager (NA)"},{"employeeNumber":"1165","lastName":"Jennings","firstName":"Leslie","extension":"x3291","email":"[email protected]","officeCode":"1","reportsTo":"1143","jobTitle":"Sales Rep"},{"employeeNumber":"1166","lastName":"Thompson","firstName":"Leslie","extension":"x4065","email":"[email protected]","officeCode":"1","reportsTo":"1143","jobTitle":"Sales Rep"},{"employeeNumber":"1188","lastName":"Firrelli","firstName":"Julie","extension":"x2173","email":"[email protected]","officeCode":"2","reportsTo":"1143","jobTitle":"Sales Rep"},{"employeeNumber":"1216","lastName":"Patterson","firstName":"Steve","extension":"x4334","email":"[email protected]","officeCode":"2","reportsTo":"1143","jobTitle":"Sales Rep"},{"employeeNumber":"1286","lastName":"Tseng","firstName":"Foon Yue","extension":"x2248","email":"[email protected]","officeCode":"3","reportsTo":"1143","jobTitle":"Sales Rep"},{"employeeNumber":"1323","lastName":"Vanauf","firstName":"George","extension":"x4102","email":"[email protected]","officeCode":"3","reportsTo":"1143","jobTitle":"Sales Rep"},{"employeeNumber":"1337","lastName":"Bondur","firstName":"Loui","extension":"x6493","email":"[email protected]","officeCode":"4","reportsTo":"1102","jobTitle":"Sales Rep"},{"employeeNumber":"1370","lastName":"Hernandez","firstName":"Gerard","extension":"x2028","email":"[email protected]","officeCode":"4","reportsTo":"1102","jobTitle":"Sales Rep"},{"employeeNumber":"1401","lastName":"Castillo","firstName":"Pamela","extension":"x2759","email":"[email protected]","officeCode":"4","reportsTo":"1102","jobTitle":"Sales Rep"},{"employeeNumber":"1501","lastName":"Bott","firstName":"Larry","extension":"x2311","email":"[email protected]","officeCode":"7","reportsTo":"1102","jobTitle":"Sales Rep"},{"employeeNumber":"1504","lastName":"Jones","firstName":"Barry","extension":"x102","email":"[email protected]","officeCode":"7","reportsTo":"1102","jobTitle":"Sales Rep"},{"employeeNumber":"1611","lastName":"Fixter","firstName":"Andy","extension":"x101","email":"[email protected]","officeCode":"6","reportsTo":"1088","jobTitle":"Sales Rep"},{"employeeNumber":"1612","lastName":"Marsh","firstName":"Peter","extension":"x102","email":"[email protected]","officeCode":"6","reportsTo":"1088","jobTitle":"Sales Rep"},{"employeeNumber":"1619","lastName":"King","firstName":"Tom","extension":"x103","email":"[email protected]","officeCode":"6","reportsTo":"1088","jobTitle":"Sales Rep"},{"employeeNumber":"1621","lastName":"Nishi","firstName":"Mami","extension":"x101","email":"[email protected]","officeCode":"5","reportsTo":"1056","jobTitle":"Sales Rep"},{"employeeNumber":"1625","lastName":"Kato","firstName":"Yoshimi","extension":"x102","email":"[email protected]","officeCode":"5","reportsTo":"1621","jobTitle":"Sales Rep"},{"employeeNumber":"1702","lastName":"Gerard","firstName":"Martin","extension":"x2312","email":"[email protected]","officeCode":"4","reportsTo":"1102","jobTitle":"Sales Rep"}]; 

Binding (instructions)

var instructs={ tag:"tr", attributes:{class:"table-row"}, props:{ email:{ tag:"td", content: null, attributes:{class:"table-data",id:"table-data-id"} }, employeeNumber:{ tag:"td", attributes:{class:"table-data"}, content: null, props:{ x:{ tag: "input", attributes:{class:"table-input"}, content: "test" } } }, extension:{ tag:"td", content: null, attributes:{class:"table-data"} }, firstName:{ tag:"td", content: null, attributes:{class:"table-data"} }, jobTitle:{ tag:"td", content: null, attributes:{class:"table-data"} }, lastName:{ tag:"td", content: null, attributes:{class:"table-data"} }, officeCode:{ tag:"td", content: null, attributes:{class:"table-data"} }, reportsTo:{ tag:"td", content: null, attributes:{class:"table-data"} } } }; 

My Function (assemble)

function assemble(r,s,n){ var n = n || new DocumentFragment(); if(typeof r !== 'string'){ //HAS CHILDREN r.forEach((o)=>{ for(y in s){ switch(y){ case "tag": var tag = document.createElement(s[y]); n.appendChild(tag); break; case "attributes": for(a in s[y]) tag.setAttribute(a,s[y][a]); break; case "content": if(s.content === null){ //append current property value } else{ tag.innerHTML = s.content; } break; case "props": for(k in o) assemble(k,s[y][k],tag); //EXECUTE PER CHILDREN break; } } }); } else{ for(x in s){ switch(x){ case "tag": var tag = document.createElement(s[x]); n.appendChild(tag); break; case "content": if(s.content === null){ //append current property value } else{ tag.innerHTML = s.content; } break; case "attributes": for(a in s[x]) tag.setAttribute(a,s[x][a]); break; case "props": for(c in s[x]) assemble(r,s[x][c],tag); break; } } return n; } return n; } document.addEventListener('DOMContentLoaded',()=>{ var data = assemble(response,instructs); console.log(data); }); 

The end result I'm looking for is an array/fragment of nested tr>td both with a class attribute and the values append to the innerHTML.

<tr class ="table-row"> <td class="table-data">how do I bind the response values?</td> <td class="table-data">how do I bind the response values?</td> <td class="table-data">how do I bind the response values?</td> <td class="table-data">how do I bind the response values?</td> <td class="table-data">how do I bind the response values?</td> <td class="table-data">how do I bind the response values?</td> <td class="table-data">how do I bind the response values?</td> </tr> 

QUESTION:

How can I bind the property values from the response to the innerHTML of the td's?

5
  • Isn't this question too broad? Try implementing it by yourself, and if you come across any specific problems, come here to ask question. Commented Apr 12, 2016 at 18:29
  • @Gothdo - I disagree. OP has given a specific example data, explanation of what he wants to achieve, and code he has so far in trying to achieve it. Commented Apr 12, 2016 at 18:31
  • @Gothdo I don't think it's to broad, I'm stuck on looping through the response while trying to map/bind the instructions/attributes to the right element. Commented Apr 12, 2016 at 18:32
  • @JordanDavis - I'd recommend that you remove (or change) those email addresses and employee ID's in your data if they are real. If they're real, it's the type of thing spammers or hackers would love to have. Commented Apr 12, 2016 at 18:40
  • 1
    @devlincarnate oh no its fake dummy data, thanks for being concern though. Commented Apr 12, 2016 at 18:41

2 Answers 2

1
+200

Give a try to the following one. Sorry, I started refactoring Yours and in the end I had rewritten it. Take care that the content is always taken as innerHTML, it would not be a bad idea at all to differentiate text(append createTextNode func outcome) and html(innerHTML prop). In your data instructs.props.employeeNumber.props.x.content should be moved into instructs.props.employeeNumber.props.x.attributes.value seen how works the input[text] tag. Btw I see there are a lot of stuff that could be discussed here. Hope it helps!

function assemble (data, instr) { var n = document.createDocumentFragment(), i; function create(d) { var objData = d; return (function _(_instr, _key, _n) { var innerHTML = !!_key && _key in objData ? objData[_key] : null, tag = null, i; if ('tag' in _instr) { tag = document.createElement(_instr.tag); tag.innerHTML = 'content' in _instr && !!_instr.content ? _instr.content : innerHTML; if ('attributes' in _instr) { for (i in _instr.attributes) tag.setAttribute(i, _instr.attributes[i]); } //recur finally if ('props' in _instr) { for (i in _instr.props) _(_instr.props[i], i, tag); } !!_n && _n.appendChild(tag); } return tag; })(instr, null); } return (function (){ for (i in data) { n.appendChild(create(data[i])); } return n; })(); } 
Sign up to request clarification or add additional context in comments.

8 Comments

You stay killin it!!! damn this is exactly what I was looking for! You are unreal. Yea I might have a few question, but first I really just need to go through it myself.
10hrs left till bounty awarded.
Need your help, with slight modification, going to put bounty on it --> stackoverflow.com/questions/37799485/…
hi @JordanDavis .. I had a quick look and it seems like You just changed both response and instructions structure ... right? I`ll get a look asap
Yea exactly, basically just changed the namespaces, props -- is now -- children and like you said response and instructions everything else basically the same, I just shorthanded your function even more. Awesome thanks, you are the man!
|
1

Though my answer does not address the code context from the question, but for structuring and binding json response i usually use regex templating. I find it simple in declaration and concise. ex,

html:

<table> <tbody id="employee"> </tbody> </table> 

js:

var template = function(tpl, data) { return tpl.replace(/\$\{([^\}]+)?\}/g, function($1, $2) { return data[$2]; }); }; var rowTemplate = '<tr class ="table-row">\ <td class="table-data">${email}</td>\ <td class="table-data"><input type="text" value=' ${employeeNumber}' /></td>\ <td class="table-data">${firstName}</td>\ <td class="table-data">${lastName}</td>\ <td class="table-data">${email}</td>\ <td class="table-data">${jobTitle}</td>\ <td class="table-data">${extension}</td>\ </tr>'; var response = [{"employeeNumber":"1002","lastName":"Murphy","firstName":"Diane","extension":"x5800","email":"[email protected]","officeCode":"1","reportsTo":null,"jobTitle":"President"}, ....] var b = document.getElementById('employee'); for (var i = 0; i < response.length; i++) { var tr = document.createElement('x'); b.appendChild(tr); tr.outerHTML = template(rowTemplate, response[i]); } 

fiddle

hope this helps.

3 Comments

Interesting, I really do like the concept you have here but doesn't really allow a good way to attach event listeners from what I see, does it? Only way I see would be declaring them inline on the html elements themselves?
yes, I get your point. the events can be bound inline or right after the tr node is created in code.
Yeah I'm trying to get away from procedural binding in the code so I can simply call assemble passing the response and instructions as parameters. I do like your concept never really thought of doing it like that, thanks for the input!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.