How to access parent scope from within a custom directive *with own scope* in AngularJS?
angularjs, angularjs-directive
Solution
See What are the nuances of scope prototypal / prototypical inheritance in AngularJS?
To summarize: the way a directive accesses its parent (`$parent`) scope depends on the type of scope the directive creates:
default (`scope: false`) - the directive does not create a new scope, so there is no inheritance here. The directive's scope is the same scope as the parent/container. In the link function, use the first parameter (typically `scope`).
`scope: true` - the directive creates a new child scope that prototypically inherits from the parent scope. Properties that are defined on the parent scope are available to the directive `scope` (because of prototypal inheritance). Just beware of writing to a primitive scope property -- that will create a new property on the directive scope (that hides/shadows the parent scope property of the same name).
`scope: { ... }` - the directive creates a new isolate/isolated scope. It does not prototypically inherit the parent scope. You can still access the parent scope using `$parent`, but this is not normally recommended. Instead, you should specify which parent scope properties (and/or function) the directive needs via additional attributes on the same element where the directive is used, using the `=`, `@`, and `&` notation.
`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.
The above link has examples and pictures of all 4 types.
You cannot access the scope in the directive's compile function (as mentioned here: https://github.com/angular/angular.js/wiki/Dev-Guide:-Understanding-Directives). You can access the directive's scope in the link function.
Watching:
For 1. and 2. above: normally you specify which parent property the directive needs via an attribute, then $watch it:
<div my-dir attr1="prop1"></div>
scope.$watch(attrs.attr1, function() { ... });
If you are watching an object property, you'll need to use $parse:
<div my-dir attr2="obj.prop2"></div>
var model = $parse(attrs.attr2);
scope.$watch(model, function() { ... });
For 3. above (isolate scope), watch the name you give the directive property using the `@` or `=` notation:
<div my-dir attr3="{{prop3}}" attr4="obj.prop4"></div>
scope: {
localName3: '@attr3',
attr4: '=' // here, using the same name as the attribute
},
link: function(scope, element, attrs) {
scope.$watch('localName3', function() { ... });
scope.$watch('attr4', function() { ... });
Problem
I'm looking for any manner of accessing the "parent" scope within a directive. Any combination of scope, transclude, require, passing in variables (or the scope itself) from above, etc. I'm totally willing to bend over backwards, but I want to avoid something totally hacky or unmaintainable. For example, I know I could do it right now by taking the `$scope` from the preLink parameters and iterating over it's `$sibling` scopes to find the conceptual "parent". What I really want is to be able to `$watch` an expression in the parent scope. If I can do that, then I can accomplish what I'm trying to do over here: AngularJS - How to render a partial with variables? An important note is that the directive must be re-usable within the same parent scope. Therefore the default behavior (scope: false) doesn't work for me. I need an individual scope per instance of the directive, and then I need to `$watch` a variable that lives in the parent scope. A code sample is worth 1000 words, so: ``` app.directive('watchingMyParentScope', function() { return { require: /* ? */, scope: /* ? */, transclude: /* ? */, controller: /* ? */, compile: function(el,attr,trans) { // Can I get the $parent from the transclusion function somehow? return { pre: function($s, $e, $a, parentControl) { // Can I get the $parent from the parent controller? // By setting this.$scope = $scope from within that controller? // Can I get the $parent from the current $scope? // Can I pass the $parent scope in as an attribute and define // it as part of this directive's scope definition? // What don't I understand about how directives work and // how their scope is related to their parent? }, post: function($s, $e, $a, parentControl) { // Has my situation improved by the time the postLink is called? } } } }; }); ```