<!DOCTYPE html>
<html>
<head>
<link data-require="bootstrap-css" data-semver="3.3.6" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css" />
<script data-require="jquery" data-semver="2.1.4" src="https://code.jquery.com/jquery-2.1.4.js"></script>
<script data-require="bootstrap" data-semver="3.3.6" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="script.js"></script>
<script>
var json = {
residus: [
{
name:'1 level XXX ', childs: [
{name:'element 1.1', childs: null},
{name:'element 1.2 jh', childs: null}
]
},
{
name:'1 level ', childs: [
{name:'element 1.1', childs: null},
{name:'element 1.2 jh', childs: [
{
name:'element 2.1.1', childs:null
},
{
name:'element 2.1.2', childs:null
}
]}
]
},
{
name:'2 level 1.. ', childs: [
{
name:'element 2.1', url: "json.js"
}
]
},
{
name:'2 level 1.. ', url: "json1.js"
}
]
};
var collapseTemplate = Object.create(CollapseTemplate);
var params = {
templateSelector: 'div.templateContainer > div.template',
parentId: 'accordion'
};
jQuery(document).ready( function() {
collapseTemplate.init(params);
collapseTemplate.load(json);
$("button").on("click", function(){
json.residus[0].name="Xavi Abarca";
collapseTemplate.load(json);
});
});
</script>
<style>
div.templateContainer {
display: none;
}
</style>
</head>
<body>
<div class="templateContainer">
<div class="panel panel-default template">
<div class="panel-heading" role="tab" id="headingOne">
<h4 class="panel-title"><input type="checkbox"/>
<a class="accordion-toggle" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
</a>
</h4>
</div>
<div id="collapseOne" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne">
<div class="panel-body"></div>
</div>
</div>
</div>
<br><br>
<div class="container">
<div class="panel-group" role="tablist" id="accordion">
</div>
<button type="button" class="btn btn-success">Change JSON</button>
</div>
</body>
</html>
// Code goes here
var CollapseTemplate = {
params : {
templateSelector : '.template',
parentId : 'accordion'
},
init : function(_params) {
//sobreescibimos los params
this.params = Utils.extend({}, this.params, _params);
},
//cargamos el JSON inicial
load : function(json) {
// borramos el panel principal
$("#" + this.params.parentId).empty();
var _this = this;
$.each(json.residus, function(i, val) {
_this.draw(val.name, val.childs, undefined, val.url);
});
},
draw : function(name, childs, panel, url) {
console.log(childs);
//pillamos un numero que se aumenta para los ids
var numId = Global.getNextNumCollapseElement();
//pillamos el template
var template = $(this.params.templateSelector);
//lo cloneamos
var $newPanel = template.clone();
var dataParentId = this.params.parentId;
//si el panel no esta definido el padre es el container definido en los params
if (panel !== undefined) {
dataParentId = $(panel).find(".panel-collapse").attr("id");
}
//añadimos una id al panel
$($newPanel).attr("id", "panel" + numId);
//pintamos elemento header
this.drawHeader(name, $newPanel, numId, dataParentId);
//manejamos los hijos
this.drawChildNodes(childs, $newPanel, numId, url);
//si es el primer nivel añadimos al padre sino al panel creado
if (panel === undefined) {
$("#" + this.params.parentId).append($newPanel.show());
} else {
$(panel).find(".panel-body").append($newPanel);
}
},
drawHeader : function(name, $newPanel, numId, dataParentId) {
$newPanel.find(".collapse").removeClass("in");
$newPanel.find(".accordion-toggle") .attr("href", "#collapse" + numId).text(name).attr( "data-parent", dataParentId).attr("id", "link-"+numId);
$newPanel.find(".panel-collapse").attr("id", "collapse" + numId).addClass("collapse").removeClass("in");
},
drawChildNodes : function(childs, $newPanel, numId, url) {
if(childs!==undefined && childs!==null) {
this.drawChildNodesArray(childs, $newPanel, numId);
} else if ((childs===undefined || childs===null)&& url!==undefined) {
this.drawChildNodesAjax(url, $newPanel, numId);
}
},
//pinta los hijos apartir de un array
drawChildNodesArray : function(childs, $newPanel, numId) {
var _this = this;
// cargamos los hijos del array
$.each(childs, function(i, val) {
if (val.childs !== null) {
_this.draw(val.name, val.childs, $newPanel, val.url);
//si se cargan los hijos por ajax
} else if (val.childs === null && val.url !== undefined && val.url !== null) {
$newPanel.find(".panel-body").append("<ul class='list-group' id='list-group-"+numId+"'><li class='list-group-item'>loading</li></ul>");
$("#" + _this.params.parentId).on('click', 'a#link-'+numId ,function() {
var jqxhr = Utils.doAjax({}, val.url);
jqxhr.done(function(dades) {
var panel = $("#" + _this.params.parentId).find("#collapse"+numId+" > div.panel-body");
$(panel).empty();
$.each(dades.residus, function(i, val) {
if(val.url===undefined || val.url===null) {
$(panel).append("<li class='list-group-item'>"+val.name+"</li>");
} else {
_this.draw(val.name, null, $("#panel"+numId), val.url);
}
});
//una vez cargado en la DOM quitamos el evento para no hacer más llamadas ajax
$("#" + _this.params.parentId).off('click', 'a#link-'+numId );
});
});
}else {
$newPanel.find(".panel-body").append("<ul class='list-group'><li class='list-group-item'>" + val.name + "</li></ul>");
}
});
},
//pinta los hijos apartir de una URL
//TODO codigo duplicado (drawChildNodesArray)
drawChildNodesAjax : function(url, $newPanel, numId) {
var _this = this;
$newPanel.find(".panel-body").append("<ul class='list-group' id='list-group-"+numId+"'><li class='list-group-item'>loading</li></ul>");
//añadimos un cklick-event para que se carquen por ajax cuando se haga click en el link
$("#" + _this.params.parentId).on('click', 'a#link-'+numId ,function() {
//una vez se ha hecho click se llama a la URL por AJAX
var jqxhr = Utils.doAjax({}, url);
//manejamos la promesa
jqxhr.done(function(dades) {
var panel = $("#" + _this.params.parentId).find("#collapse"+numId+" > div.panel-body");
$(panel).empty();
$.each(dades.residus, function(i, val) {
if(val.url===undefined || val.url===null) {
$(panel).append("<li class='list-group-item'>"+val.name+"</li>");
} else {
_this.draw(val.name, null, $("#panel"+numId), val.url);
}
});
//una vez cargado en la DOM quitamos el evento para no hacer más llamadas ajax
$("#" + _this.params.parentId).off('click', 'a#link-'+numId );
});
});
}
};
Global = {
countCollapseElements: 0,
getNextNumCollapseElement : function() {
return this.countCollapseElements++;
}
};
Utils = {
// extender un object con otro, sobrescribe los properties que se llaman
// igual
extend : function(dest) {
var sources = Array.prototype.slice.call(arguments, 1);
sources.forEach(function(source) {
Object.keys(source).forEach(function(key) {
dest[key] = source[key];
});
});
return dest;
},
// hacer la llamada ajax con JSON
doAjax : function(params, _url) {
return $.ajax({
url : _url,
dataType : 'json',
data : params,
cache : false,
error : function(jqXHR, textStatus, errorThrown) {
alert(textStatus);
}
});
}
};
/* Styles go here */
{
"residus": [
{
"name": "child 1",
"childs": null
},
{
"name": "child 2",
"childs": null
}
]
}
{
"residus": [
{
"name": "child 1",
"childs": null,
"url": "json.js"
},
{
"name": "child 2",
"childs": null,
"url": "json.js"
}
]
}