0

I am trying to build a dynamic Tree component, i.e. like a folder structure. I would like the children to be load dynamically, so in contrast to other modules expecting the whole structure to be present during initialization, I would like to load the elements when needed.

Therefore I bind a controller function to the directive. This is my HTML:

<tree-model data="treedata" load-children="loadTree(n)" />

And this defines the tree-model directive

app.directive( 'treeModel', function( $compile, $log, $http ) {
      return {
        restrict: 'E',
        scope: {
          node: '=data',
          loadChildren: '&'
        },
        link: function ( scope, element, attrs ) {
          var template =
            '<ul>' +
              '<li ng-model="node">' +
              '<span ng-switch="node.status">' +
                '<i ng-switch-when="collapsed" class="collapsed" ng-click="selectNodeHead(node)">C</i>' +
                '<i ng-switch-when="expanded" class="expanded" ng-click="selectNodeHead(node)">E</i>' +
                '<i ng-switch-default="" class="normal">N</i> ' +
              '</span>' +
              '<span ng-class="node.selected">{{node.label}}</span>' +
              '<span ng-repeat="c in node.children">' +
                '<tree-model data="c" load-children="loadChildren(n)" />' +
              '</span>' +
              '</li>' +
            '</ul>';

          var repl = function() {
            var newElement = angular.element(template);
            $compile(newElement)(scope);
            element.replaceWith(newElement);
          }

          scope.selectNodeHead = function(selectedNode) {
            if (selectedNode.children && selectedNode.children.length) {
              // Collapse
              selectedNode.children = undefined;
              selectedNode.hasChildren = true;
              selectedNode.status = "collapsed";
            } else {
              // Expand
              scope.loadChildren({n: selectedNode});
              selectedNode.status ="expanded";
              repl();
            }
          };

          repl();
        }
      };
    });

This is my controller:

app.controller('MyCtrl',
  function ($scope, $log, $http) {
    $scope.treedata = { "label": "Root", "status": "collapsed", "screen": "Root", "id": "999999", "hasChildren": true };

    $scope.loadTree = function(node) {
      $scope.output = "Node: " + node;
      node.children =[
        { "label": "Child 1", "status": "collapsed", "screen": "Child 1", "id": "999997", "hasChildren": true},
        { "label": "Child 2", "status": "normal", "screen": "Child 2", "id": "999995", "hasChildren": false}
      ];
    };
  });

So I'd like to recursively build the tree. This works well for the first node and my controller function loadTree() gets the expected node as input. However, in the child step it calls loadTree(), but without passing node as parameter.

This seems to be somehow related to isolated scopes. If I do

scope.$parent.$parent.loadChildren({n: selectedNode});

instead of

scope.loadChildren({n: selectedNode});

in the expand part of the directive, the parameter passing works.

Here is a plunker to demonstrate the issue: http://plnkr.co/edit/i4vDuCd7efmweZ9IZXMD

1 Answer 1

1

The problem is in how you call your method inside the template in a directive. It should be called like this:

'<tree-model data="c" load-children="loadChildren({n : n})" />'

A slightly modified code of yours here: http://jsfiddle.net/yyankowski/2zcpp/3/

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

1 Comment

Wow, thank you so much! So simply and logical in the end, but I might have never though of this...

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.