I have hierarchical data like this:
[
{
"children":[
{
"children":[...],
[...]
},
{
"children":[...],
[...]
},
],
[...]
}
]
I want to build tree-like grid by flattening that data. I am using following directives:
app.directive('tree', function (hierarchyService, logger, $timeout) {
return {
scope: {
data: '='
},
restrict: 'E',
replace: true,
template: '<div>' +
'<table class="table table-striped table-hover">' +
' <thead>' +
' <tr>' +
' <th class="col-md-6">Account name</th>' +
' </tr>' +
' </thead>' +
' <tbody><tr collection data="data" /></tbody>' +
' </table>' +
'</div>'
};
});
app.directive('collection', function() {
return {
restrict: "A",
replace: true,
scope: {
data: '=',
depth: '@'
},
template: '<member ng-repeat="member in data" member="member" depth="{{depth}}" />',
link: function (scope, element, attrs) {
scope.depth = parseInt(scope.depth || 0);
}
}
});
app.directive('member', function($compile) {
return {
restrict: "E",
replace: true,
scope: {
depth: '@',
member: '='
},
template: '<tr ng-class="{selected: member.selected}">' +
'<td>' +
' <span ng-show="depth > 0" style="width: {{depth * 16}}px; display: inline-block;"></span> {{member.name}}' +
'</td>' +
'</tr>',
link: function (scope, element, attrs) {
scope.depth = parseInt(scope.depth || 0);
if (angular.isArray(scope.member.children) && scope.member.children.length > 0) {
var el = angular.element('<tr collection data="member.children" depth="{{newDepth}}" />');
scope.depth = parseInt(scope.depth || 0);
scope.newDepth = scope.depth + 1;
$compile(el)(scope);
// Flatten hierarchy, by appending el to parent
element.parent().append(el);
}
}
}
});
The problem is that, in collection added from link
method, depth
from parent scope is appended to newDepth
. As a result depth
for level 3 nodes has value depth="3 2 1 "
.
How to disable inheriting of depth
?
I have also noticed, that when I change replace
to false in collection
and member
directives, depth works as intended, but then HTML is invalid.
Sometimes much simpler solutions are better. It is better to flatten tree like structures in service and then iterate over new structure using ng-repeat. https://plnkr.co/edit/CiFGZYi6NdH8ZFDiAyPz?p=preview
Much simpler code. There is no need for all those directives that make it hard to understand. Also you should not use replace with directives as it is deprecated.
To set style dynamically you should use ng-style directive.
var app = angular.module('app', []);
app.factory('treeFlatting', function () {
function flattenTree(tree, depth) {
depth = depth || 0;
return tree.reduce(function (rows, node) {
var row = {
name: node.name,
depth: depth,
};
var childrenRows = angular.isArray(node.children) ?
flattenTree(node.children, depth + 1) : [];
return rows.concat(row, childrenRows);
}, []);
}
return flattenTree;
});
app.directive('tree', function (treeFlatting) {
return {
restrict: 'E',
replace: true,
template: '<div>' +
'<table class="table table-striped table-hover">' +
' <thead>' +
' <tr>' +
' <th class="col-md-6">Account name</th>' +
' <th class="col-md-1">Depth</th>' +
' </tr>' +
' </thead>' +
' <tbody>'+
' <tr ng-repeat="row in rows">'+
' <td ng-style="{\'padding-left\': (16 * row.depth) + \'px\'}">{{row.name}}</td>'+
' <td>{{row.depth}}</td>'+
' </tr>'+
' </tbody>' +
' </table>' +
'</div>',
link: function (scope, element, attrs) {
scope.data = [
{
name: 'Root',
children: [
{
name: 'Level 1',
children: [
{
name: 'Level 2'
}
]
}
]
}
];
scope.rows = treeFlatting(scope.data).filter(function (row) {
return row.depth > 0;
});
}
};
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With