Reputation: 787
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?
Upvotes: 39
Views: 22123
Reputation: 638
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
};
});
Upvotes: 1
Reputation: 2914
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
Upvotes: 2
Reputation: 1711
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 )
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.
Upvotes: 27
Reputation: 1362
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();
}
}});
Upvotes: 15