2

What is the preferred way to link/bind two directives together? I have a controller with two directives, first directive is a select element, after selecting option, second directive should process selected item value.

App code:

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

app.controller('MainCtrl', function() {
  var sharedData = { selectedId: '' };

  var vm = this;

  vm.sharedData = sharedData;
});

app.directive('directiveA', ['$compile', function($compile) {
  return {
    restrict: 'E',
    scope: {
      selectedId: '='
    },
    template: '<select data-ng-model="vm.sharedData.selectedId" data-ng-options="currentSelect.Id as currentSelect.Name for currentSelect in vm.sharedData.availableSelects track by currentSelect.Id"><option value="">Select option</option></select><p>Directive A, selected ID: {{vm.sharedData.selectedId}}</p>',
    bindToController: true,
    controllerAs: 'vm',
    controller: function() {
      vm = this;

      vm.sharedData = {
        availableSelects: [
          {Id:1, Name: 'Option 1'},
          {Id:2, Name: 'Option 2'},
          {Id:3, Name: 'Option 3'},
          {Id:4, Name: 'Option 4'}
        ]
      }
      vm.logMessage = logMessage;

      function logMessage(selectedId) {
        console.log('directiveA: ' + selectedId);
      }
    },
    link: function($scope, elem, attr, ctrl) {
      attr.$observe('selectedId', function(selectedId) {
        ctrl.logMessage(selectedId);
      });
    }
  };
}]);

app.directive('directiveB', ['$compile', function($compile) {
  return {
    restrict: 'E',
    scope: {
      selectedId: '='
    },
    template: '<p>Directive B, selected ID: {{vm.sharedData.selectedId}}</p>',
    bindToController: true,
    controllerAs: 'vm',
    controller: function() {
      vm = this;

      vm.logMessage = logMessage;

      function logMessage(selectedId) {
        console.log('directiveB: ' + selectedId);
      }
    },
    link: function($scope, elem, attr, ctrl) {
      attr.$observe('selectedId', function(selectedId) {
        ctrl.logMessage(selectedId);
      });
    }
  };
}]);

HTML code:

<!DOCTYPE html>
<html data-ng-app="plunker" data-ng-strict-di>
  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link href="style.css" rel="stylesheet" />
    <script data-semver="1.4.1" src="https://code.angularjs.org/1.4.1/angular.js" data-require="[email protected]"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl as vm">
    <p>MainCtrl, selected ID: {{vm.sharedData.selectedId}}</p>
    <directive-a data-selected-id="vm.sharedData.selectedId"></directive-a>
    <directive-b data-selected-id="vm.sharedData.selectedId"></directive-b>
  </body>

</html>

Here is a Plunker example:

http://plnkr.co/edit/KVMGb8uAjUwD9eOsv72z?p=preview

What I'm doing wrong?

Best Regards,

4
  • You have two directives which require isolated scope on the same element which is not allowed. This a duplicate question. stackoverflow.com/questions/20470662/… Commented Jun 22, 2015 at 20:18
  • IF i understood your requirement correctly , think u need to can use require for the refernces between two controllers, stackoverflow.com/questions/30673459/… Commented Jun 22, 2015 at 20:23
  • Hmm, the stackoverflow.com/questions/20470662/… is about having two directives on the same element (<input>), but here I have two independent directives on same variable. Commented Jun 22, 2015 at 21:02
  • I would like to avoid using require parameter - this two directives should be independent. Commented Jun 22, 2015 at 21:03

1 Answer 1

1

The key issue revolves around your use of isolated scopes:

scope: {
  selectedId: '='
},

With controllerAs binding:

controllerAs: 'vm',

What this essentially does, to put it basically, is it places the view model onto the directives scope, accessed through the alias you assign in the controllerAs. So basically in your html when you go:

<directive-a data-selected-id="vm.sharedData.selectedId"></directive-a>

You are actually accessing the directive-a view model, NOT the MainCtrl view model. BECAUSE you set directive-a as having an isolate scope... which is a new scope, isolated from the MainCtrl.

What you need to do is more along the following lines:

http://plnkr.co/edit/wU709MPdqn5m2fF8gX23?p=preview


EDIT

TLDR: I would recommend having unique view model aliases (controllerAs) when working with isolated scopes to properly reflect the fact that they are not the same view model.

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

4 Comments

Just to add: Basically what I did was have the MainCtrl vm store the selectedId, which is then two-way bound to both child directives through the MainCtrl view model (mainVm). This property is then bound to the child directives corresponding view models: aVm and bVm. When they change or access these properties they will in fact be accessing the parent mainVm.selectedId property due to the two-way binding in the isolate scope declarations.
Thanks, this is what I need! I forget about distinct names for "controllerAs" model.
Glad I could help :) understanding scoping is always a big part of angular, and controllerAs (along with bindToController) goes a long way in helping to remove the ambiguity of which objects/variables are being bound. So it's great to see you trying to use this pattern.
Also marking the post as the answer would be greatly appreciated as well if the post helped ;) all the best!

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.