Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular appends parent attribute value

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.

like image 354
Krzysztof Avatar asked May 17 '16 07:05

Krzysztof


1 Answers

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;
          });
        }
    };
});
like image 111
sielakos Avatar answered Oct 31 '22 09:10

sielakos