AngularJS - accessing parent directive properties from child directives

16,956

Solution 1

Taking inspiration from this SO post, I've got a working solution here in this plunker.

I had to change quite a bit. I opted to have an isolated scope on the editableString as well because it was easier to bind in the correct values to the template. Otherwise, you are going to have to use compile or another method (like $transclude service).

Here is the result:

JS:

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

myApp.controller('Ctrl', function($scope) {

  $scope.myModel = { property1: 'hello1', property2: 'hello2' }

});


myApp.directive('editableFieldset', function () {
  return {
    restrict: 'E',
    scope: {
      model: '='
    },
    transclude: true,
    replace: true,
    template: '<div class="editable-fieldset" ng-click="edit()"><div ng-transclude></div></div>',
    link: function(scope, element) {
      scope.edit = function() {

        scope.editing = true;
      }
    },
    controller: ['$scope', function($scope) {

      this.getModel = function() {
        return $scope.model;
      }

    }]
  };
});

myApp.directive('editableString', function () {
  return {
    restrict: 'E',
    replace: true,
    scope: {
      label: '@',
      field: '@'
    },
    template: '<div><label>{{ label }}</label><p>{{ model[field] }}</p></div>',
    require: '^editableFieldset',
    link: function(scope, element, attrs, ctrl) {

      scope.model = ctrl.getModel();
    }
  };
});

HTML:

  <body ng-controller="Ctrl">
    <h1>Hello Plunker!</h1>
    <editable-fieldset model="myModel">
      <editable-string label="Some Property1:" field="property1"></editable-string>
      <editable-string label="Some Property2:" field="property2"></editable-string>
    </editable-fieldset>
  </body>

Solution 2

You can get access to parent controller by passing attribute in child directive link function

link: function (scope, element, attrs, parentCtrl) {
    parentCtrl.$scope.editing = true;
}
Share:
16,956

Related videos on Youtube

Rahul Sekhar
Author by

Rahul Sekhar

Updated on June 14, 2022

Comments

  • Rahul Sekhar
    Rahul Sekhar about 2 years

    This should not be too hard a thing to do but I cannot figure out how best to do it.

    I have a parent directive, like so:

    directive('editableFieldset', function () {
      return {
        restrict: 'E',
        scope: {
          model: '='
        },
        replace: true,
        transclude: true,
    
        template: '
          <div class="editable-fieldset" ng-click="edit()">
            <div ng-transclude></div>
    
            ...
    
          </div>',
    
        controller: ['$scope', function ($scope) {
          $scope.edit = ->
            $scope.editing = true
    
           // ...
        ]
      };
    });
    

    And a child directive:

    .directive('editableString', function () {
      return {
        restrict: 'E',
        replace: true,
    
        template: function (element, attrs) {
          '<div>
            <label>' + attrs.label + '</label>
            <p>{{ model.' + attrs.field + ' }}</p>
    
            ...
          </div>'
        },
        require: '^editableFieldset'
      };
    });
    

    How can I easily access the model and editing properties of the parent directive from the child directive? In my link function I have access to the parent scope - should I use $watch to watch these properties?

    Put together, what I'd like to have is:

    <editable-fieldset model="myModel">
      <editable-string label="Some Property" field="property"></editable-string>
      <editable-string label="Some Property" field="property"></editable-string>
    </editable-fieldset>
    

    The idea is to have a set of fields displayed by default. If clicked on, they become inputs and can be edited.

  • Rahul Sekhar
    Rahul Sekhar over 10 years
    Thank you. This doesn't really help to access a dynamic property, like editing though. I guess I would have to use $watch to keep track of that in my child directive?
  • Rahul Sekhar
    Rahul Sekhar over 10 years
    Also, I would think for constant, unchanging attributes like field and label here, passing them to a function that generates the template would be faster than adding them as bindings? There's no need to waste time on each digest cycle checking for those bindings that won't change, right?
  • Davin Tryon
    Davin Tryon over 10 years
    Isn't editing a property of the child? Or do you want to only edit one child at a time?
  • Rahul Sekhar
    Rahul Sekhar over 10 years
    I'd like to edit all children at a time. Editing is a property of the parent that I'd like the children to be able to read and change their state depending on its value.
  • Davin Tryon
    Davin Tryon over 10 years
    Yes, some sort of $watch in the child. Probably exposed through the parent controller.
  • Leonardo
    Leonardo over 10 years
    you get this fourth param if you: ... require: "^parentCtrl"
  • Harish
    Harish about 9 years
    Yes and the concept is explained in the section "Creating Directives that Communicate" in the documentation for directives link