Reputation: 1177
We have this directive which receives a dictionary.
We were thinking that a directivy would be able of calling itself recursively, but it is not working.
Our current data structure is:
var cubicApp = angular.module('CubicApp', ['autocomplete']).controller('PreviewForecastCtrl', function ($scope, $http) {
$scope.name = 'Thiago';
$scope.performance = {
headers: [{ name: '', colspan: 1 }, { name: 'Week result', colspan: 6 }, { name: '2017', colspan: 13 }, { name: 'General', colspan: 4 }],
subheaders: [
{ name: 'Product', colspan: 1 },
{ name: '29-May', colspan: 1 },
{ name: '30-May', colspan: 1 },
{ name: '31-May', colspan: 1 },
{ name: '01-Jun', colspan: 1 },
{ name: '02-Jun', colspan: 1 },
{ name: 'Total', colspan: 1 },
{ name: 'Jan', colspan: 1 },
{ name: 'Feb', colspan: 1 },
{ name: 'Mar', colspan: 1 },
{ name: 'Apr', colspan: 1 },
{ name: 'May', colspan: 1 },
{ name: 'Jun', colspan: 1 },
{ name: 'Jul', colspan: 1 },
{ name: 'Aug', colspan: 1 },
{ name: 'Sep', colspan: 1 },
{ name: 'Oct', colspan: 1 },
{ name: 'Nov', colspan: 1 },
{ name: 'Dec', colspan: 1 },
{ name: 'Year', colspan: 1 },
{ name: '1M', colspan: 1 },
{ name: '6M', colspan: 1 },
{ name: '12M', colspan: 1 },
{ name: 'Accum.', colspan: 1 }
],
books: [
{
name: 'Renda Variável',
css: 'primary',
totals: Array.from({length: 23}, () => Math.random()),
books: [
{
name: 'Ibovespa Ativo',
css: 'active',
totals: Array.from({ length: 23 }, () => Math.random()),
books: [
{
name: 'BOVA11 (Equity)',
css: '',
totals: Array.from({ length: 23 }, () => Math.random()),
books: []
},
{
name: 'Cash BRL (Cash)',
css: '',
totals: Array.from({ length: 23 }, () => Math.random()),
books: []
}
]
}
]
}
]
};
$scope.loadInfo = function () {
};
});
The directives are:
cubicApp.directive('drillDownTable', function ($document) {
return {
restrict: 'E',
transclude: true,
scope: {
data: '=',
},
templateUrl: '../../Scripts/app/templates/drill-down-table.html'
};
}).directive('inner', function ($document, $compile) {
return {
restrict: 'A',
transclude: true,
scope: {
inner: '=',
},
templateUrl: '../../Scripts/app/templates/drill-down-inner-table.html'
};
});;
drill-down-table.html
:
<table class="table table-hover table-bordered jambo_table">
<thead>
<tr>
<th ng-repeat="h in data.headers" colspan="{{h.colspan}}" style="text-align:center !important">{{h.name}}</th>
</tr>
<tr>
<th ng-repeat="h in data.subheaders" colspan="{{h.colspan}}" style="text-align:center !important">{{h.name}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="book in data.books" inner="book"></tr>
</tbody>
</table>
drill-down-inner-table.html
:
<tr>
<td>{{inner.name}}</td>
<td ng-repeat="t in inner.totals">{{t | number : 2}}</td>
</tr>
<tr ng-repeat="book in inner.books" inner="book"></tr>
Upvotes: 1
Views: 118
Reputation: 15399
I think restricting the directive type to A
and putting it on the same element as the ng-repeat
is causing the issue. When I made the directive type E
, and changed the templates it worked (see Plunker below).
Of course moving the directive to an element inside the ng-repeat causes problems with keeping everything in the table aligned because you're trying to recursively traverse an object and output <tr />
's.
My recommendation would be to write a recursive function that converts the recursive data.books
structure into an array that will nicely play with a single ng-repeat
. You might be able to get your original approach to work, but I suspect converting it to an array will take less time than creating a recursive directive that works correctly. Main downside is keeping the recursive structure up-to-date if data.books
changes. To address this, my recommendation would be to figure out ahead of time what all can cause data.books
to change (so you don't have to $watch
it) and call the recursive function every time it's changed.
Another alternative might be to use some CSS to style what I have below into something that visually is what you want (give proper widths to all the columns, worry about borders, etc.).
http://plnkr.co/edit/pxCWxyfGtHxfAZG5FiVJ?p=preview
HTML:
<!DOCTYPE html>
<html ng-app="CubicApp">
<head>
<script data-require="[email protected]" data-semver="1.6.2" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="PreviewForecastCtrl">
<drill-down-table data="performance"></drill-down-table>
</body>
</html>
Main:
angular.module('autocomplete', []);
var cubicApp = angular.module('CubicApp', ['autocomplete']).controller('PreviewForecastCtrl', function ($scope, $http) {
$scope.name = 'Thiago';
$scope.performance = {
headers: [{ name: '', colspan: 1 }, { name: 'Week result', colspan: 6 }, { name: '2017', colspan: 13 }, { name: 'General', colspan: 4 }],
subheaders: [
{ name: 'Product', colspan: 1 },
{ name: '29-May', colspan: 1 },
{ name: '30-May', colspan: 1 },
{ name: '31-May', colspan: 1 },
{ name: '01-Jun', colspan: 1 },
{ name: '02-Jun', colspan: 1 },
{ name: 'Total', colspan: 1 },
{ name: 'Jan', colspan: 1 },
{ name: 'Feb', colspan: 1 },
{ name: 'Mar', colspan: 1 },
{ name: 'Apr', colspan: 1 },
{ name: 'May', colspan: 1 },
{ name: 'Jun', colspan: 1 },
{ name: 'Jul', colspan: 1 },
{ name: 'Aug', colspan: 1 },
{ name: 'Sep', colspan: 1 },
{ name: 'Oct', colspan: 1 },
{ name: 'Nov', colspan: 1 },
{ name: 'Dec', colspan: 1 },
{ name: 'Year', colspan: 1 },
{ name: '1M', colspan: 1 },
{ name: '6M', colspan: 1 },
{ name: '12M', colspan: 1 },
{ name: 'Accum.', colspan: 1 }
],
books: [
{
name: 'Renda Variável',
css: 'primary',
totals: Array.from({length: 23}, () => Math.random()),
books: [
{
name: 'Ibovespa Ativo',
css: 'active',
totals: Array.from({ length: 23 }, () => Math.random()),
books: [
{
name: 'BOVA11 (Equity)',
css: '',
totals: Array.from({ length: 23 }, () => Math.random()),
books: []
},
{
name: 'Cash BRL (Cash)',
css: '',
totals: Array.from({ length: 23 }, () => Math.random()),
books: []
}
]
}
]
}
]
};
$scope.loadInfo = function () {
};
});
Directives:
cubicApp.directive('drillDownTable', function ($document) {
return {
restrict: 'E',
transclude: true,
scope: {
data: '=',
},
templateUrl: 'drill-down-table.html'
};
}).directive('inner', function ($document, $compile) {
return {
restrict: 'E',
transclude: true,
scope: {
inner: '=',
},
templateUrl: 'drill-down-inner-table.html'
};
});;
drill-down-table.html
:
<table class="table table-hover table-bordered jambo_table">
<thead>
<tr>
<th ng-repeat="h in data.headers" colspan="{{h.colspan}}" style="text-align:center !important">{{h.name}}</th>
</tr>
<tr>
<th ng-repeat="h in data.subheaders" colspan="{{h.colspan}}" style="text-align:center !important">{{h.name}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="book in data.books">
<td colspan="{{data.subheaders.length}}">
<inner inner="book"></inner>
</td>
</tr>
</tbody>
</table>
drill-down-inner-table.html
:
<table>
<tbody>
<tr>
<td colspan="{{ inner.totals.length }}">{{inner.name}}</td>
</tr>
<tr>
<td ng-repeat="t in inner.totals">{{t | number : 2}}</td>
</tr>
<tr ng-repeat="book in inner.books" >
<td colspan="{{ inner.totals.length }}">
<inner inner="book" ng-if="book"></inner>
</td>
</tr>
</tbody>
</table>
Note ng-if="book"
probably isn't necessary; I added that when I troubleshooting something.
Upvotes: 1