0

I'm trying to implement a recursive directive and it seems like to get it to work nicely I need to define an isolate scope as well as have access to the parent scope. Basically I want my directive to have access to variables set as attributes on the directive itself, but i also want to be able to access variables and methods set in the controller's scope. Is there a way to combine the two? I've tried with transclude but i suppose i'm not entirely sure if i've used it properly. Here is a fiddle example of my problem, where i'd like each 'child' in the directive to be able to call the function sayHi(): http://jsfiddle.net/n8dPm/655/

stackPusher
  • 6,076
  • 3
  • 35
  • 36
  • This would be much easier with a Component (need v1.5+) instead of a Directive. – 1.618 Dec 21 '17 at 17:25
  • Components let you bind functions, but it breaks when the component is recursive. Events work though: https://plnkr.co/edit/cZEUsmVjWje3knx4vfHs?p=preview – 1.618 Dec 21 '17 at 18:08

1 Answers1

1

You have to pass the sayHi function to your directive. Directives create their own scope, So sayHi function is not part of your directive scope, the way to allow it is by creating a new prop an pass it.

HTML

<div ng-app="myapp">
    <div ng-controller="TreeCtrl">
        <tree family="treeFamily"
          say-hi="sayHi(name)"
         ngTransclude></tree>
    </div>
</div>

JS

var module = angular.module('myapp', []);

module.controller("TreeCtrl", function($scope) {
    $scope.treeFamily = {
        name : "Parent",
        children: [{
            name : "Child1",
            children: [{
                name : "Grandchild1",
                children: []
            },{
                name : "Grandchild2",
                children: []
            }]
        }, {
            name: "Child2",
            children: []
        }]
    };
    $scope.sayHi = function(name){
        alert(name+' says hello!')
    }
});

module.directive("tree", function($compile) {
    return {
        restrict: "E",
        scope: {
            family: '=',
          sayHi : '&'
         },
        transclude: true,
        template: 
            '<p>{{ family.name }}</p>'+
            '<ul>' + 
                '<li ng-repeat="child in family.children">' + 
                    '<tree family="child" say-hi="sayHi(name)"></tree>' +
                    '<button ng-click="sayHi({name : child.name})">Say Hi</button>' +
                '</li>' +
            '</ul>',
        compile: function(tElement, tAttr) {
            var contents = tElement.contents().remove();
            var compiledContents;
            return function(scope, iElement, iAttr) {
                if(!compiledContents) {
                    compiledContents = $compile(contents);
                }
                compiledContents(scope, function(clone, scope) {
                         iElement.append(clone); 
                });
            };
        }
    };
});
AngelSalazar
  • 3,080
  • 1
  • 16
  • 22
  • Thanks, this solution works. It also made me realize directives probably aren't the solution for my problem. In reality my child elements need access to a lot of variables and methods from the parent scope and to pass every one of them down like this weighs down my directive. Ultimately I went with a template based solution described here https://stackoverflow.com/a/11861030/2963703 – stackPusher Dec 22 '17 at 15:30