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="angular.js@1.4.x"></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,

Sassa
  • 159
  • 2
  • 11
  • You have two directives which require isolated scope on the same element which is not allowed. This a duplicate question. http://stackoverflow.com/questions/20470662/angularjs-two-directives-on-one-element – Ernesto Rendon Jun 22 '15 at 20:18
  • IF i understood your requirement correctly , think u need to can use require for the refernces between two controllers, http://stackoverflow.com/questions/30673459/using-require-in-directive-to-require-a-parent-controller/30673703#30673703 – Shushanth Pallegar Jun 22 '15 at 20:23
  • Hmm, the http://stackoverflow.com/questions/20470662/angularjs-two-directives-on-one-element is about having two directives on the same element (), but here I have two independent directives on same variable. – Sassa Jun 22 '15 at 21:02
  • I would like to avoid using require parameter - this two directives should be independent. – Sassa Jun 22 '15 at 21:03

1 Answers1

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.

Paul Popiel
  • 930
  • 11
  • 21
  • 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. – Paul Popiel Jun 23 '15 at 03:20
  • Thanks, this is what I need! I forget about distinct names for "controllerAs" model. – Sassa Jun 23 '15 at 07:04
  • 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. – Paul Popiel Jun 23 '15 at 08:13
  • Also marking the post as the answer would be greatly appreciated as well if the post helped ;) all the best! – Paul Popiel Jun 23 '15 at 08:14