Rahul Sekhar
Rahul Sekhar

Reputation: 2841

AngularJS - accessing parent directive properties from child directives

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.

Upvotes: 11

Views: 17212

Answers (2)

user3036090
user3036090

Reputation: 81

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;
}

Upvotes: 8

Davin Tryon
Davin Tryon

Reputation: 67296

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>

Upvotes: 8

Related Questions