Edit in JSFiddle

console.clear();

/**
 * Common Helper
 */
var nivo = {};

// Event Delegation Wrapper
nivo.on = function(node, evtName, nodeName, callback){
  var args = [].slice.call(arguments);  
  callback = args.length == 3 ? args[2] : callback;
  nodeName = args.length == 3 ? null : nodeName;
  
	node.addEventListener(evtName, function(e){
  	var target = e.target;
    
    if(nodeName === null){
    	callback.call(target, e);
      return;
    }
    
    while(target !== null){
      if(target.nodeName === nodeName.toUpperCase()){
				callback.call(target, e);
        break;
      }
      target = target.parentNode;
    }
  });
};

/**
 * Global Listeners
 */
nivo.on(document, 'click', 'a', function(e){
	var callable = this.getAttribute('nv-click');
  if (callable === null) return;
  eval(callable);
});

nivo.on(document, 'keyup', function(){
	var 
  	model = this.getAttribute('nv-model'),
    field = this.field,
		value = this.value;
    
  if(model === null) return;

  field.setModel(model, value);
});

/**
 * Node
 */
var Node = function(data){
	for(var key in data){
  	this[key] = data[key];
  }
}
Node.prototype.save = function(){
	console.log('save:', this.id, this.contents);
}

/**
 * Tree
 */
var Tree = function(){
	this.nodes = [];
  this.tree = [];
  this._id_current = null;
  this._node_current = this.tree;
}

Tree.prototype.addNode = function(){
	for(var i in arguments){
  	this.nodes.push(arguments[i]);
  }
  return this;
}

Tree.prototype.build = function(){
	this._buildBranch({branch: this.tree, id_current: null});
  this._sort({branch: this.tree});
  return this;
}

/**
 * find the next child and append it to the parent
 * @param {object} args  {branch:{object}, id_current: {int}}
 */
Tree.prototype._buildBranch = function(args){
	var
  	branch = args.branch,
    id_current = args.id_current;
    
	for(var key in this.nodes){
  	if(this.nodes[key].id_parent === id_current){
    	var node = this.nodes[key];
			branch.push(node); // unsorted push
      node.children = [];
      this._buildBranch({branch: node.children, id_current: node.id});
    }
  }
}

/**
 * find the next child in the assigned order and append it to the parent
 */
Tree.prototype._sort = function(args){
	var branch = args.branch;
  
  branch.sort(function(a, b){
  	return (a.id === b.id_next || a.id_next === null) ? 1 : 0;
  	//return (a.id === b.id_next) ? 1: 0;
  });
	
  // sort all children (recursion)
	for(var key in branch){
		this._sort({branch: branch[key].children});
  }
}

Tree.prototype.render = function(){
	var tree = this._renderBranch(this.tree)	
  console.log(tree);
  // append template to view
  var
  	element = document.createElement('div'),
    target = document.querySelector('#tree');
  
  element.innerHTML = tree.trim() ;
  element = element.firstChild;
	target.innerHTML = '';
  target.appendChild(element);
}

Tree.prototype._renderBranch = function(branch){
	var tmpl_branch = '';
	for(var key in branch){
  	tmpl_branch += this._renderLeaf(branch[key]);
  }
  if(tmpl_branch !== ''){
  	tmpl_branch = '<ul>'+ tmpl_branch +'</ul>';
  }
  return tmpl_branch;
}

Tree.prototype._renderLeaf = function(leaf){
	console.log(leaf)
	// compile template
	
  var tmpl_children = this._renderBranch(leaf.children);
  
  var tmpl_raw = document.querySelector('#tmpl__tree__node').innerHTML;
  tmpl_raw = tmpl_raw.replace('{{name}}', leaf.name);
  tmpl_raw = tmpl_raw.replace('{{children}}', tmpl_children);
  return tmpl_raw;
}

/**
 * Retreive node by id
 * @param {int} id
 * @param {string|undefined} compare_key  The identifier that will be used to retreive the node. Useful when you request a node by its id_parent or id_next instead of just its id
 */
Tree.prototype._getNode = function(id, compare_key){
	if(compare_key === undefined){
  	compare_key = 'id';
  }
	for(var key in this.nodes){
  	if(this.nodes[key].id !== id) continue;
    return this.nodes[key];
  }
  throw "Node with id "+ id +" was not found";
}

Tree.prototype.moveNode = function(node, id_target_prev){
	// get previous node
  	// update previous nodes next to node in hand next
  // find parent_new
  	// assign node in hands next to parent_new next
    // update parents new next
  //var node_previous = this._getNode(node.id, 'id_next');
  
  node.name += ' - has been changed';
  
}

/**
 * Field
 */
var Field = function(fieldname, fieldtitle, fieldtype, field_tmpl_sel){
	this.replacements = {
  	name: fieldname,
		title: fieldtitle
  };
	
	this.type = fieldtype;
  this.tmpl_sel = field_tmpl_sel;
  this.element = null; // will set once template got appended to the dom
}
Field.prototype.render = function(target_sel){
	// compile template
	var tmpl_raw = document.querySelector(this.tmpl_sel).innerHTML;
  for(var field in this.replacements){
  	tmpl_raw = tmpl_raw.replace('{{'+field+'}}', this.replacements[field]);
  }
  // append template to view
  var element = document.createElement('div');
  element.innerHTML = tmpl_raw.trim() ;
  element = element.firstChild;
  document.querySelector(target_sel).appendChild(element);
  // expose instance
  var self = this;
  [].forEach.call(element.querySelectorAll('[nv-model]'), function(element){
  	element.field = self;
  });
}

Field.prototype.setModel = function(key, value){
	if(this.replacements[key] === undefined){
  	throw "can not set '"+ key +"' in model";
  }
  this.replacements[key] = value;
	console.log(key, value);
}

/**
 * Template
 */
var Template = function(id){
	var args = [].splice.call(arguments,0);
	this.id = args.splice(0,1)[0];
  this.fields = [];
  for(var arg in args){
  	this.fields.push(args[arg]);
  }
}
Template.prototype.render = function(target){
	for(var i in this.fields){
		this.fields[i].render(target);
  }
}

/**
 * Controller
 */

var root_tmpl = new Template(
  1,
  new Field('title', 'Der Titel', 'text', '#tmpl__field--text'),
  new Field('copy', 'Der Text', 'text', '#tmpl__field--textarea')
)
root_tmpl.render('main');

console.log(root_tmpl);

var root_node = new Node({
  id: 1,
  id_root: null,   // reference to the top parent for fast access 
  id_parent: null, // reference to the parent to maintain a hirarchy
  id_next: 4,   // reference to the next sibling node to maintain an order
  id_template: 1,
  
  alias: 'home',
  name: 'startseite',
  contents: {
    title: 'der Titel',
    copy: 'lorem ipsum dolor'
  },
});

var container_node = new Node({
  id: 2,
  id_root: 1,
  id_parent: 1,
  id_next: null,
  alias: 'home_container',
  name: 'Billboard',
  contents: null,
});

var block_node = new Node({
  id: 3,
  id_root: 1,
  id_parent: 2,
  id_next: 5,
  alias: 'home_container_block A',
  name: 'Billboard Slide A',
  contents: {
    image: 'images/image.png',
    title: 'a great image'
  }
});

var block_node2 = new Node({
  id: 5,
  id_root: 1,
  id_parent: 2,
  id_next: 7,
  alias: 'home_container_block B',
  name: 'Billboard Slide B',
  contents: {
    image: 'images/image.png',
    title: 'a great image'
  }
});

var block_node3 = new Node({
  id: 7,
  id_root: 1,
  id_parent: 2,
  id_next: 8,
  alias: 'home_container_block C',
  name: 'Billboard Slide C',
  contents: {
    image: 'images/image.png',
    title: 'a great image'
  }
});

var block_node4 = new Node({
  id: 8,
  id_root: 1,
  id_parent: 2,
  id_next: null,
  alias: 'home_container_block D',
  name: 'Billboard Slide D',
  contents: {
    image: 'images/image.png',
    title: 'a great image'
  }
});

var node_products = new Node({
  id: 4,
  id_root: null,
  id_parent: null,
  id_next: null,
  alias: 'products',
  name: 'Produkte',
  contents: {
    title: 'der Titel',
    copy: 'lorem ipsum dolor'
  },
});

var node_imprint = new Node({
  id: 6,
  id_root: null,
  id_parent: null,
  id_next: 1,
  alias: 'imprint',
  name: 'Imprint',
  contents: {
    title: 'der Titel',
    copy: 'lorem ipsum dolor'
  },
});

window.foo = function(){
	alert(123);
}


setTimeout(function(){
	//console.clear();
	//tree.moveNode(block_node3, 2);
  //tree.render();
}, 1000);



console.log(tree.tree);
node_products.save();

//console.log(tree.tree)


(function(){
  var element = document.querySelector('[nv-controller="TreeController"]');
  element.$scope = this;
	
  var tree = new Tree();
	tree
  	.addNode(node_products, container_node, root_node, block_node4, block_node, block_node3, node_imprint, block_node2 )
	  .build()
    .render();
  
  element.innerHTML = element.innerHTML.replace(/\{\{([^\}\}]+)\}\}/, 'hi');
}());
<div id="app">
  <nav nv-controller="TreeController">
    {{tree.length}}
    <ul id="tree">
      <!-- tree will be rendered here -->
    </ul>
  </nav>
  
  <main>
    <!-- form will be rendered here -->
  </main>
</div>

<script type="text/template" id="tmpl__field--text">
	<div>
		<label>{{title}}</label>
		<input nv-model="name" type="text" name="{{name}}" value="" />
   </div>
</script>

<script type="text/template" id="tmpl__field--textarea">
	<div>
		<label>{{name}}</label>
		<textarea nv-model="name" name="{{name}}"></textarea>
   </div>
</script>

<script type="text/template" id="tmpl__tree__node">
<li>
{{name}} <a href="#" nv-click="foo()">down</a>
{{children}}
</li>
</script>