1

The goal here is to have two different directives that are technically siblings share functionality. I will either use one or the other, never one inside the other.

However, the second directive will have all the capability of the first with some small additions. Because of this, I would like the functionality to inherit from the "Parent" directive to the "Child".

I'm achieving this by re-using the same directive definition object from the Parent on the Child, with the exception of the controller/template fields being changed.

This was all working well up until I hit the watchers from my ParentDirCtrl. For some reason the watcher seems to be set up correctly watching mydir.obj1 and yet somehow inside the watcher callback function mydir.obj1 becomes undefined.

I'm assuming something about _.extend/$controller is changing how the $scopes work so mydir.obj1 isn't defined in the ParentDirCtrl, but I'm not sure why that would be the case.

Plunk

angular.module('plunker', [])

// lodash
.constant('_', _)

.controller('MainCtrl', function($scope, $timeout) {
  $scope.obj = {
    name: 'John',
    age: 30,
  };
})


.controller('ParentDirCtrl', function($scope) {
  var mydir = this;

  mydir.doStuffInParent = function() {
    alert('executed from the parent directive');
  }

  $scope.$watch('mydir.obj1', function() {
    // ====================================
    //              ERROR
    // Why is 'mydir.obj1' undefined when
    // occupation is set?  
    // ====================================
    mydir.obj1.occupation = 'Meteorologist';
  });
})


.directive('parentDirective', parentDirective)


.directive('childDirective', function() {
  // borrow the directive definition object from the parent directive
  var parentDDO = parentDirective();

  // uodate the template and controller for our new directive
  parentDDO.template = [
    '<div>', 
      '<p ng-click="mydir.doStuffInParent()">{{mydir.obj1.name}}</p>',
      '<p ng-click="mydir.doStuffInChild()">{{mydir.obj1.age}}</p>',
    '</div>'
    ].join('');

  parentDDO.controller = function($scope, $controller, _) {
      // extend 'this' with the Parent's controller
      var mydir = _.extend(this, $controller('ParentDirCtrl', { $scope: $scope }));

      mydir.doStuffInChild = function() {
        alert("executed from the child directive");
      };
  }; 

  return parentDDO;
});


// this will be moved to the top during declaration hoisting
function parentDirective() {
  return {
    restrict:'E',
    scope: {},
    bindToController: {
      obj1: '=',
    },
    template: '<div>{{mydir.obj1}}</div>',
    controller: 'ParentDirCtrl',
    controllerAs: 'mydir',
  };
}
4
  • If they are essentially the same why can't you just differentiate functionality all in one by setting an attribute? Commented Jul 20, 2016 at 21:56
  • The child is going to have some functions that overwrite the parent's and then a bunch of functionality simply not needed in the parent. Additionally it's going to have some watchers for attributes that don't exist on the parent. That being said, 100% of the functionality in the parent is needed in the child. It would take a lot of conditionals to control all of this in a single directive. Commented Jul 20, 2016 at 22:06
  • var mydir is this extended with the properties from the parent controller. My understanding is that this is the way to inherit from another controller. Commented Jul 20, 2016 at 22:48
  • oops i read it wrong Commented Jul 20, 2016 at 22:51

1 Answer 1

1

obj1 is populated on the child controller instance - that's why mydir.obj1 is undefined in the parent watcher. You can access obj1 directly via scope or by using the reference passed into the watcher:

$scope.$watch('mydir.obj1', function(val) {
    $scope.mydir.obj1.occupation = 'Meteorologist';
    // or
    val.occupation = 'Meteorologis';
});

There is no scope inheritance here - both controllers operate on the same scope. Controller-AS syntax is what confuses you - I'd get rid of it to make things clearer.

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

3 Comments

Thanks, I took your advice and updated to ditch controller-As syntax. The error went away but now the watcher doesn't fire at all: plnkr.co/edit/oVCoPW?p=preview It seems that after the $timeout that watcher should fire.
To catch changes made by angular.copy - set the third parameter (objectEquality) of the $watch method to true. angular.copy does not change the reference of the destination.
ah, duh. Should have been using $watchCollection. Thanks!

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.