2

When using directives, I sometimes find the need to pass data through nested directives, even though the intermediate directives don't use the data, they need to pass it down to their child directives. This is annoying because it couples directives and makes the wiring quite hard.

Consider this example: The MainCtrl holds some data in an array and uses a 'first-directive'. This directive then uses 'second-directive' which needs access to the data from MainCtrl. Therefore, 'first-directive' needs to get the data from MainCtrl and pass it through - and itself it does nothing with the data:

<body ng-controller="MainCtrl as mainCtrl">
    <first-directive data="mainCtrl.items"></first-directive>
</body>

and the javascript:

app.controller('MainCtrl', function($scope) {
  var self = this;
  self.items = ["first", "second"];
});
app.directive('firstDirective', function() {
    return {
        scope: {
          data: '='
        },
        template: '<second-directive data="data"></second-directive>'
    };
});
app.directive('secondDirective', function() {
    return {
        scope: {
            data: '='
        },
        template: 'second directive data: {{data}}'
    };
});

How could the above code be changed such that 'first-directive' does not need to know about data? Is this a common problem in angular? How is this usually solved? The problem gets even worse when there is more nesting of directives involved.

Plunker: https://plnkr.co/edit/aKWBq5DLOLFvamk6gx4e?p=preview

Edit: I've found a blog post discussing this. It recommend the use of 'require' on the nested directives. What do you think? http://www.codelord.net/2014/03/30/writing-more-maintainable-angular-dot-js-directives/

user3346601
  • 1,019
  • 1
  • 11
  • 18

4 Answers4

1

You can achieve this by storing what you need to pass in a service that can be injected into both directives. Here is a good example of someone else doing it:

https://stackoverflow.com/a/13482919/5349719

Community
  • 1
  • 1
cullimorer
  • 755
  • 1
  • 5
  • 23
  • Correct me if I'm wrong, but doesn't this mean that every instance of the directive will get the same data? Which, in many cases, is not what you want. – qwelyt Aug 19 '16 at 13:10
  • I'm not sure I follow, why wouldn't you want them to share the data? – cullimorer Aug 19 '16 at 13:16
  • The service is a singleton, but you can expose a method or an object to get a particular subset of that data: ex. service.get( 'section_12' ) – Simone Poggi Aug 19 '16 at 13:25
  • Say I have a page where I want do display 2 `table`s. The tables should contain different types of data, otherwise they should be identical. The directive that holds the lists uses a sub-directive to do something, say create the column heads in some fancy way. The column heads and data differs, and the heads are useless for the actual `table` (well, in our made up scenario they are), so passing the data to a service wouldn't be possible as it would return the same data for both sub-directives. Using a reference like @Motocarota suggest still means the superdirective needs to passa data to sub. – qwelyt Aug 19 '16 at 13:55
  • You could have a service with many functions that could save the data in different variables? – cullimorer Aug 19 '16 at 14:02
  • @cullimorer How would the directive know which variable to use? – qwelyt Aug 19 '16 at 15:09
  • 1
    Because they would use different retrieval methods. eg. function getVariable1() returns something different to getVariable()... Am I understanding the question correctly? – cullimorer Aug 19 '16 at 15:19
  • @cullimorer How can the sub-directive, that is the same for all wrapping directives, know which function to call without you passing data through the wrapping directive? The OP wants a way for the subdirective to get data without the wrapping(super) handling the data. If the sub can never get any info via the wrapper, how will it ever know which function it should call? – qwelyt Aug 20 '16 at 13:20
1

You can remove scope from all your directives, you don't need isolate scope in all cases... when you remove the scope property... you can access controller variable directly in your child directive.

scope: {
  data: '='
},

That's how prototyal inheritance works in JS, making a directive isolate you are breaking the chain.. then when you need to pass data to nested child directive you have do some sort of like this.

Most of the case, you don't need to isolate your scope itself.

Thalaivar
  • 23,282
  • 5
  • 60
  • 71
1

you could set the scope to true to access the scope of the parent controller. although you'll have to make the items a property of $scope. see this plunk for example.

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

app.controller('MainCtrl', function($scope) {
  // var self = this;
  // self.items = ["first", "second"];
  $scope.items = ["first", "second"];
});


app.directive('firstDirective', function() {
    return {
      scope:true,
        template: '<second-directive></second-directive>'
    };
});

app.directive('secondDirective', function() {
    return {
        scope:true,
        template: 'second directive data: {{items}}'
    };
});
<!DOCTYPE html>
    <html ng-app="plunker">
    
      <head>
        <meta charset="utf-8" />
        <title>AngularJS Plunker</title>
        <script>document.write('<base href="' + document.location + '" />');</script>
        <link rel="stylesheet" href="style.css" />
        <script data-require="angular.js@1.4.x" src="https://code.angularjs.org/1.4.12/angular.js" data-semver="1.4.9"></script>
        <script src="app.js"></script>
      </head>
    
      <body ng-controller="MainCtrl as mainCtrl">
        <first-directive></first-directive>
      </body>
    
    </html>
Mridul Kashyap
  • 704
  • 1
  • 5
  • 12
1

I updated plnker and I refer to this answer for both directive is set

scope: false

default (scope: false) - the directive does not create a new scope, so there is no inheritance here. The directive's scope is the same scope as the parent/container. In the link function, use the first parameter (typically scope). copied

Community
  • 1
  • 1