1

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>
6
  • why not simply iterate "data.inner" ?? like ng-repeat=total in data.inner.totals Commented Jun 9, 2017 at 19:04
  • because each book child might have books Commented Jun 9, 2017 at 19:05
  • Oh!! that's true. console is showing you error?. Commented Jun 9, 2017 at 19:09
  • no, there is no error unfortunataly. I found some threads about, but it i not working for me. Commented Jun 9, 2017 at 19:12
  • This could be useful stackoverflow.com/questions/14430655/… (implement a service I test the punlker adding "childrens to inner childrens") Commented Jun 9, 2017 at 19:13

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

3 Comments

I took a while to get this working, but it worked amazingly! Thank you so much! I am very glad.
@Thiago You're welcome! I'm glad I could help! Did you end up using the recursive directive? If so, I'm impressed.
well, it is being used so far, but I wonder whether the ux guys will leave that there

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.