11

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
  • 2,761
  • 5
  • 23
  • 27

2 Answers2

8

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>
Community
  • 1
  • 1
Davin Tryon
  • 66,517
  • 15
  • 143
  • 132
  • 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 Nov 26 '13 at 11:19
  • 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? – Rahul Sekhar Nov 26 '13 at 11:22
  • Isn't editing a property of the child? Or do you want to only edit one child at a time? – Davin Tryon Nov 26 '13 at 11:23
  • 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. – Rahul Sekhar Nov 26 '13 at 11:24
  • Yes, some sort of `$watch` in the child. Probably exposed through the parent controller. – Davin Tryon Nov 26 '13 at 11:33
8

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;
}
  • 3
    you get this fourth param if you: ... require: "^parentCtrl" – Leonardo Mar 24 '14 at 23:22
  • 1
    Yes and the concept is explained in the section "**Creating Directives that Communicate**" in the documentation for directives [link](https://docs.angularjs.org/guide/directive) – Harish Jun 03 '15 at 06:35