Custom child directive accessing scope of parent

21,976

Solution 1

The city directive $parent is a transcluded scope of state directive.

The transcluded scope of the state directive is inherit for $parent of state directive which is controller thus that is why $parent.MyName = India.

The $parent of transcluded scope is the state directive isolated scope ( scope = {} ) that is why $parent.$parent.MyName = Tamilnadu ( Part of Angular 1.3 update )

enter image description here

Bit of detail of what happen : How to access parent scope from within a custom directive *with own scope* in AngularJS?

transclude: true - the directive creates a new "transcluded" child scope, which prototypically inherits from the parent scope. If the directive also creates an isolate scope, the transcluded and the isolate scopes are siblings. The $parent property of each scope references the same parent scope.

Angular v1.3 update: If the directive also creates an isolate scope, the transcluded scope is now a child of the isolate scope. The transcluded and isolate scopes are no longer siblings. The $parent property of the transcluded scope now references the isolate scope.

Also Matthew's answer is correct for parent-child directive communications.

Solution 2

Does this work for you? Adapted from this answer.

There isn't a straightforward way to access the transcluded content's parent element, so we inject the parent controller into the child to access its scope.

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

  app.controller("CountryCtrl",function($scope){
      $scope.myName = "India";
  });

  app.controller("StateCtrl",function($scope){
  });

  app.directive("state",function(){return {
      restrict : 'E',
      transclude: true,
      scope : { myName  : '=nameofthestate'},
      template:"**   {{myName}} is inside {{$parent.myName}}<br/><ng-transclude></ng-transclude>",
      controller: function ($scope) {
        this.getName = function () {
          return $scope.myName;
        }
      }
  }});

  app.directive("city",function(){return {
      restrict : 'E',
      require:'^state',
      scope : { myName  : '=nameofthecity'},
      template:"****   {{myName}} is inside {{parentName}} which is in {{$parent.myName }}<br/> ",
      link: function(scope, element, attrs, ctrl) {
        scope.parentName = ctrl.getName();
      }
  }});

Solution 3

When AngularJS encounters transclude , it clones the HTML before replacing it with the template or templateUrl contents. Then, when it encounters ng-transclude , it compiles the transcluded content, but links it to the parent scope instead of the isolated scope of the directive. Thus, the transcluded content still has access to the parent controller and its content, while the directive HTML has an isolated scope (or a new scope, as the case might be).

AngularJS Up and Running

Solution 4

Check out sollution of my directive, it works with a lot of parrents. What I done was to remove transclude and require params. Do not bother about dirty html, just watch js, simple as f.. :D

    CRM.directive('inputwv', function ($compile) {
    var getTemplate = function(contentType) {
        var template = '';

        switch(contentType) {
                case '3':
                    template = '<input type="number" ng-init="inputHide[$parent.$index][$index]=false" ng-blur="inputHide[$parent.$index][$index]=false" ng-Enterd="updateRecord(row[0], $parent.$index)" ng-Enteru="inputHide[$parent.$index][$index]=false" ng-model="row[$index]" ng-change="row[$index]" ng-value="row[$index]" ng-Right-Click="click(element, $index, $parent.$index )" ng-esc="inputHide[$parent.$index][$index]=false" style="cursor:cell;border-bottom:0px;width:100px">'
                    break;
                case '0':
                    template = '<input type="text" ng-init="inputHide[$parent.$index][$index]=false" ng-blur="inputHide[$parent.$index][$index]=false" ng-Enterd="updateRecord(row[0], $parent.$index)" ng-Enteru="inputHide[$parent.$index][$index]=false" ng-model="row[$index]" ng-change="row[$index]" ng-value="row[$index]" ng-Right-Click="click(element, $index, $parent.$index )" ng-esc="inputHide[$parent.$index][$index]=false" style="cursor:cell;border-bottom:0px">'
                    break;
                case '1':
                    template = '<input type="text" ng-init="inputHide[$parent.$index][$index]=false" ng-blur="inputHide[$parent.$index][$index]=false" ng-Enterd="updateRecord(row[0], $parent.$index)" ng-Enteru="inputHide[$parent.$index][$index]=false" ng-model="row[$index]" ng-change="row[$index]" ng-value="row[$index]" ng-Right-Click="click(element, $index, $parent.$index )" ng-esc="inputHide[$parent.$index][$index]=false" style="cursor:cell;border-bottom:0px">'
                    break;
                case '2':
                    template = '<textarea class="materialize-textarea teal-text" type="text" ng-init="inputHide[$parent.$index][$index]=false" ng-blur="inputHide[$parent.$index][$index]=false" ng-Enterd="updateRecord(row[0], $parent.$index)" ng-Enteru="inputHide[$parent.$index][$index]=false" ng-model="row[$index]" ng-change="row[$index]" ng-value="row[$index]" ng-Right-Click="click(element, $index, $parent.$index )" ng-esc="inputHide[$parent.$index][$index]=false" style="cursor:cell;border-bottom:0px">'
                    break;
                case '4':
                    template = '<input type="text" ng-init="inputHide[$parent.$index][$index]=false" ng-blur="inputHide[$parent.$index][$index]=false" ng-Enterd="updateRecord(row[0], $parent.$index)" ng-Enteru="inputHide[$parent.$index][$index]=false" ng-model="row[$index]" ng-change="row[$index]" ng-value="row[$index]" ng-Right-Click="click(element, $index, $parent.$index )" ng-esc="inputHide[$parent.$index][$index]=false" style="cursor:cell;border-bottom:0px">'
                    break;
                case '5':
                    template = '<input type="date" class="datepicker" ng-init="inputHide[$parent.$index][$index]=false" ng-blur="inputHide[$parent.$index][$index]=false" ng-Enterd="updateRecord(row[0], $parent.$index)" ng-Enteru="inputHide[$parent.$index][$index]=false" ng-model="row[$index]" ng-change="row[$index]" ng-value="row[$index]" ng-Right-Click="click(element, $index, $parent.$index )" ng-esc="inputHide[$parent.$index][$index]=false" style="cursor:cell;border-bottom:0px"><script type="text/javascript">$(\'.datepicker\').pickadate({selectMonths: true, selectYears: 15});</script>'
                    break;
                default:
                    template = '<textarea class="materialize-textarea teal-text" type="text" ng-init="inputHide[$parent.$index][$index]=false" ng-blur="inputHide[$parent.$index][$index]=false" ng-Enterd="updateRecord(row[0], $parent.$index)" ng-Enteru="inputHide[$parent.$index][$index]=false" ng-model="row[$index]" ng-change="row[$index]" ng-value="row[$index]" ng-Right-Click="click(element, $index, $parent.$index )" ng-esc="inputHide[$parent.$index][$index]=false" style="cursor:cell;border-bottom:0px">'
            }

        return template;
}

    var linker = function(scope, element, attrs) {
        element.html(getTemplate(attrs.typ)).show();

        $compile(element.contents())(scope);
    }

    return {
        restrict: "E",
        link: linker
    };
});
Share:
21,976
Sriram
Author by

Sriram

BY DAY : WEB DEVELOPMENT BY NIGHT : WEB DEVELOPMENT BY FUN : WEB DEVELOPMENT

Updated on June 28, 2020

Comments

  • Sriram
    Sriram about 4 years

    I am having two custom directives in my angularJS app. One act as a parent and other act as child. I am trying to access parent's scope inside child directive. But I am not getting the desired output.

    <div ng-controller="CountryCtrl">
    {{myName}}
        <div ng-controller="StateCtrl">
            <state nameofthestate="'Tamilnadu'">
                <city nameofthecity="'Chennai'"></city>
            </state>
        </div>
    </div>
    

    and my script looks like

    var app = angular.module("sampleApp",[]);
    app.controller("CountryCtrl",function($scope){
        $scope.myName = "India";
    });
    app.controller("StateCtrl",function($scope){
    });
    app.directive("state",function(){return {
        restrict : 'E',
        transclude: true,
        scope : { myName  : '=nameofthestate'},
        template:"**   {{myName}} is inside {{$parent.myName}}<br/><ng-transclude></ng-transclude>"
    }});
    app.directive("city",function(){return {
        restrict : 'E',
        require:'^state',
        scope : { myName  : '=nameofthecity'},
        template:"****   {{myName}} is inside {{$parent.myName}} which is in {{$parent.$parent.myName }}<br/> "
    }});
    

    Corresponding JSFiddle available in https://jsbin.com/nozuri/edit?html,js,output

    The output which i am getting is

    India
    ** Tamilnadu is inside India
    **** Chennai is inside India which is in Tamilnadu
    

    and the expected output is

    India
    ** Tamilnadu is inside India
    **** Chennai is inside Tamilnadu which is in India
    

    Can anyone educate me what i am doing wrong here?

  • Sriram
    Sriram almost 9 years
    yes. It works. But i am accepting @kwan245's answer as it explains what mistake i did.. :) Thanks Metthew
  • Anton Rand
    Anton Rand over 7 years
    I've used this approach, but in my case the variable holding the name is an array that may change. Is there a way of dealing with this situation as the view doesn't update for me in this case? I know I could use watches but would want to avoid this as much as possible. I have an example here: stackoverflow.com/questions/42676713/…