0

I'm attempting to build a dynamic data-binding function called assemble which takes (2) input parameters:

  1. server response (JSON) - nested json object.
  2. instruction set (JSON) - a configuration object which controls the binding.

The Problem: The function currently doesn't bind nested json.

The Question: What do I need to change to make it able to support the desired output?


The current output:

<form class="myForm"> <div class="myFirstName">john</div> <div class="myLastName">doe</div> <div>[email protected]</div> <div>this is a static inject element</div> <div class="myNestedContainer"> //HERE IS MY PROBLEM //RETURNS undefined <span class="myUserAge">undefined</span> <span class="myUserDob">undefined</span> <span class="myRace">undefined</span> </div> </form> <form class="myForm"> <div class="myFirstName">jane</div> <div class="myLastName">doe</div> <div>[email protected]</div> <div>this is a static inject element</div> <div class="myNestedContainer"> //HERE IS MY PROBLEM //RETURNS undefined <span class="myUserAge">undefined</span> <span class="myUserDob">undefined</span> <span class="myRace">undefined</span> </div> </form> 

The desired output:

<form class="myForm"> <div class="myFirstName">john</div> <div class="myLastName">doe</div> <div>[email protected]</div> <div>this is a static inject element</div> <div class="myNestedContainer"> <span class="myUserAge">26</span> <span class="myUserDob">1990</span> <span class="myRace">white</span> </div> </form> <form class="myForm"> <div class="myFirstName">jane</div> <div class="myLastName">doe</div> <div>[email protected]</div> <div>this is a static inject element</div> <div class="myNestedContainer"> <span class="myUserAge">25</span> <span class="myUserDob">1991</span> <span class="myRace">white</span> </div> </form> 

The server response:

response=[ { first: "john", last: "doe", email: "[email protected]", profile:{ age: "26", dob: "1990", race: "white" } }, { first: "jane", last: "doe", email: "[email protected]", profile:{ age: "25", dob: "1991", race: "white" } } ] 

The instruction set:

instruction={ tag:"form", attributes:{"class":"myForm"}, children:{ first:{ tag:"div", attributes:{"class":"myFirstName"}, content: null }, last:{ tag:"div", attributes:{"class":"myLastName"}, content: null }, email:{ tag:"div", content: null }, myFakeTag:{ tag:"div", content: "this is a static inject element" }, profile:{ tag:"div", attributes:{"class":"myNestedContainer"}, children:{ age:{ tag:"span", attributes:{"class":"myUserAge"}, content: null }, dob:{ tag:"span", attributes:{"class":"myUserDob"}, content: null }, race:{ tag:"span", attributes:{"class":"myRace"}, content: null } } } } } 

The assemble function:

assemble=(data,instr)=>{ var instr = (typeof instr !== "string") ? instr : instr.split('.').reduce((o,i)=>o[i], model); var n = new DocumentFragment(); var gen=(d)=>{ var o = d; return(_=(_instr,k,_n)=>{ for(b in _instr){ switch(b){ case "tag": var tag = document.createElement(_instr[b]); break; case "attributes": for(a in _instr[b]){ tag.setAttribute(a,_instr[b][a]); } break; case "events": for(a in _instr[b]){ _instr[b][a].forEach((l)=>{ tag.addEventListener(a,l); }); } break; case "content": tag.innerHTML = (_instr[b]===null) ? o[k] : _instr[b]; break; case "children": for(var _i in _instr[b]){ _(_instr.children[_i],_i,tag); } break; } !!_n && !!tag && _n.appendChild(tag); } return tag; })(instr, null); }; (()=>{ for(i in data){ var test = gen(data[i]); n.appendChild(test); } })(); return n; } 

1 Answer 1

1
+100

In the end the only thing that changes is how you want the instructions to be used and extended. These are a bit different from the previous but one important thing is that the appendChild should not be inside the instructions attributes loop for the node but right after outside it; some attention must be payed to some special attributes as well, maybe class is not the only one, who knows :) ... try to completely replace the inner for block with the following :

var tag = null, a; if ('tag' in _instr) { tag = document.createElement(_instr.tag); if ('attributes' in _instr) for(a in _instr.attributes) { a.match(/^class$/) && (a = 'className'); tag.setAttribute(a,_instr.attributes[a]); } if ('events' in _instr) for(a in _instr.events) tag.addEventListener(a,_instr.events[a], false); // // if ('content' in _instr && _instr.content!==null) // tag.innerHTML = _instr.content; // // but take care ... what if is a input[text] tag[_instr.tag=='input' ? 'value' : 'innerHTML'] = ('content' in _instr && _instr.content !== null) ? _instr.content : o[k]; if ('children' in _instr) for(a in _instr.children) _(_instr.children[a], a, tag); !!_n && !!tag && _n.appendChild(tag); } 

==================

UPDATED

Now the output now is exactly the one expected. I even fixed a stupid bug handling the class attribute. Try it out, maybe even on other inputs, I tried to put text instead of null on some data and it looks fine. See You!

function assemble (data, instr) { var n = document.createDocumentFragment(), i; function create(d) { return (function _(_instr, _d, _key, _n) { var tag = null, i; if ('tag' in _instr) { tag = document.createElement(_instr.tag); tag.innerHTML = 'content' in _instr && !!_instr.content ? _instr.content : typeof _d == 'string' ? _d : ''; if ('attributes' in _instr) for (i in _instr.attributes) tag.setAttribute(i, _instr.attributes[i]); if ('events' in _instr) for(i in _instr.events) tag.addEventListener(i,_instr.events[i], false); //recur finally if ('children' in _instr) { for (i in _instr.children){ _(_instr.children[i], _d[i], i, tag); } } !!_n && _n.appendChild(tag); } return tag; })(instr, d, 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.

20 Comments

Ok I just copied that in and it's not putting any innerHTML in any of them. The way I had it originally just so your aware was content: null meant to use the value as in the current prop: value, I could also if I wanted to statically add a string in like this content: 'my string', then lastly if I didn't want anything I wouldn't add the content prop at all.
What do you want as html?? The key??... just || k in the assignation
Yea the current key:value. When I console.log o[k] it gives exactly what I want but if its nest (sub-level) like how age,dob,race are then it just gives me the object.
To make the comment above more clear, an example would be, in the response.user object the first prop/key has the value 'john' so if I add the content attribute with the value null in the instruction.user object, with null meaning append the value "john".
I'll add a sample output to the question, one sec.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.