When you attach any controller to DOM node, for example,
<div ng-controller="myController">{{testProperty}}</div>
controller scope will be associated with this DOM node.
Imagine another example. We created directive myDirective, which has its own isolated scope. Now we have
<div ng-controller="myController" myDirective>{{testProperty}}</div>
Which scope will be associated with DOM node in this case? Scope of controller or directive? And last example, we have one more directive mySecondDirective with its own isolated
scope
<div myDirective mySecondDirective>{{testProperty}}</div>
Which scope will be associated with DOM node in this case? Scope of myDirective or mySecondDirective? And can you explain why?
<div myDirective mySecondDirective>{{testProperty}}</div>
for multiple directives defined on a single DOM element, you can explicitly define a priority: - see this: https://docs.angularjs.org/api/ng/service/$compile#directive-definition-object
<div ng-controller="myController" myDirective>{{testProperty}}</div>
Which scope will be associated with DOM node in this case? Scope of
controller or directive?
for controller's $scope and a directive's scope defined on a single DOM element: "The transclude option changes the way scopes are nested. It makes it so that the contents of a transcluded directive have whatever scope is outside the directive, rather than whatever scope is on the inside. In doing so, it gives the contents access to the outside scope." from the angularjs docs found here: https://docs.angularjs.org/guide/directive#creating-a-directive-that-wraps-other-elements
Thanks, but all of you are wrong. As I found out it is not possible to create two or more scopes on one DOM node. Angular throws this error:
Error: [$compile:multidir] Multiple directives [pTest2, pTest] asking for new/isolated scope on: <p-test p-test2="">
Related
I have create a custom directive that displays the directive template within the current view:
<div class="upper-outfits-layer" ng-show="outfitExpanded">
<expanded-outfit outfit="outfits[currentOutfit]"></expanded-outfit>
</div>
That will display an html template. The view this directive is placed within, has its own controller. I need to access the views scope variables from this directive and vice versa.
Is it possible to access a sperate controller from a custom directive?
Do not use isolated scope in your directive. You can directly get access your view scope.
(In case of Isolated scope) Pass the variables in the attribute of your directive. They will be accessible to your directive through your isolated scope.
(In case of Isolated scope) Use scope.$parent in your directive to access view scope.
If you don't isolate the directive's scope using scope:{}, you should be able to access parent controller's variables. Maybe add the directive code to your question if the problem persists...
I was trying to understand scope in angularjs.
Say while registering a directive in angularjs if we dont provide any scope as the property of the object, what is the scope of the object then?
For example consider the following code:-
app.directive("kid", function() {
return {
restrict: "E",
template: '<input type="text" ng-model="chore"> {{chore}}'
};
});
Now say if i have 2 elements in my html:-
<kid></kid>
<kid></kid>
So how do above end up sharing the same scope? I am not able to find convincing answer yet.
Yes, As you didn't declared any scope option of directive, it will share the same scope.
Here is Demo Plunkr
Now come to the point, what is scope object?
scope object in Angular is nothing having context information and that will available on html, can also be utilized to provide two way binding. Basically scope is binded with some controller.
When things comes to directive scope, if you didn't mention scope property inside directive, that means directive shares the scope of the controller where the directive element has been placed.
To make them treated as a different scope for each directive you could create an an directive with an isolated scope, which can be defined using scope: {} inside a directive, when you define a scope: {} inside a directive, it creates an isolated child scope which is not prototypically inherited from the parent scope using $scope.$new(true) method.
Plunkr with isolated scope
Your question is about scope inheritance and isolate scope.
If you do declare a scope property on a directive object then the directive has its own isolate scope.
If you don't declare a scope property on your directive object the directive inherits the scope of the scope it was instantiated in.
So with your definition of the kid directive that doesn't declare an isolate scope the kid directives in the code example below both inherit the scope of the controller that they are instantiated in.
<div ng-controller="myCtrl">
<kid></kid>
<kid></kid>
</div>
Scope is an object that refers to the application model. It is an execution context for expressions. Scopes are arranged in hierarchical structure which mimic the DOM structure of the application. Scopes can watch expressions and propagate events.
Scope characteristics
Scopes provide APIs ($watch) toenter image description here observe model mutations.
Scopes provide APIs ($apply) to propagate any model changes through
the system into the view from outside of the “Angular realm”
(controllers, services, Angular event handlers).
Scopes provide context against which expressions are evaluated.
For example {{username}} expression is meaningless, unless it is evaluated against a specific scope which defines the username property.Scope is the glue between application controller and the view.
I am trying to make a transitive transclusion, or call it “directive inception”.
I made this example to illustrate what I am trying to do:
http://plnkr.co/edit/0hFFHknDps2krtK1D9ud?p=preview
The directive “first” wraps the directive “second” in its template and the two of them use transclusion.
What I want to do is to bind a value from a controller to the html that is a child of the “first” directive.
So I wanted my example to display:
<h1>Chained transclusions test</h1>
<div>
<h2>First directive</h2>
<div>
<h2>Second directive</h2>
<div>Controller hello</div>
</div>
</div>
Obviously that is not what I got.
I tried to analyze the scope with the developer tool and I was surprised by the result scope tree:
the result trees
I thought angularJS would create a new scope when using the transclude feature in a directive. And that this scope would be a non isolate sibling of my directive isolate scope. But I cannot see any sibling of my first directive scope (although it uses transclude). Plus, every children of my “first” directive has a scope isolated from the controller scope since the “first” directive scope is an isolated one.
I don’t understand the behavior here.
Is the transclude inclusion completely forbidden in angularJS ?
Is it possible to create a directive with transclusion, that wraps another directive that uses transclusion ?
It seems to me that this is the whole power behind web components, the fact that transclusion or any other special caracteristics should be seen as “implementation detail”, and the component should be able to use other directives that hide their own implementation details.
Without getting into the details of scope creation when using isolate scope with transclusion... it is possible to nest transclusions, but in your example, you need to make scope.controllerMsg available to the first directive's isolate scope:
JS:
app.directive('first', function(){
return {
...
scope: { controllerMsg: '=text'},
...
}
});
HTML:
<first text="controllerMsg">
{{controllerMsg}}
</first>
Demo
What does the scope parameter in the link property of the directive definition object represent. Is it the scope that the directive will be used in or is it an isolated scope for every time the directive is used?
If you do not define scope in your directive then it has access to your parent scope. If you define it as
scope: {}
Then an isolate scope is created.
Should be able to see more in the docs, but that is a basic rundown.
https://docs.angularjs.org/guide/directive
Given a directive (container1) with transclude and an isolated scope, when the directive is linked then I have these scopes:
Scope 004 <-- scope of the body
Scope 005 <-- scope of directive container1
Scope 006 <-- scope of the ng-transclude
I expected:
Scope 004 <-- scope of the body
Scope 005 <-- scope of the directive
Scope 006 <-- scope of the ng-transclude
If the same directive has a shared scope instead of an isolated scope, I get the expected result.
This causes me a problem because, if the transcluded content contains another directive (component1) with an isolated scope, I get:
Scope 004 <-- scope of the body
Scope 005 <-- scope of the directive
Scope 006 <-- scope of the ng-transclude
Scope 007 <-- scope of directive component1
I want to use the directives like this:
<container1>
<component1 data="objectExposedInContainer1"/>
</container1>
But that does not work, inside component1, $scope.data is undefined because objectExposedInContainer1 is not on the right scope.
I have two questions:
Why ng-transclude's scope is not a child of its directive's scope if the directive has an isolated scope? Is this a bug?
If it's not a bug, how can a container directive pass data to it's content, if not by setting attributes like I tried.
Here is a sample where it does not work: http://plnkr.co/edit/NDmJiRzTF9e5gw8Buht2?p=preview. Because Plunker is built with Anguar, it's hard to debug with Batarang. I recommend downloading the code locally. Comment out line 10 of app.js to make it work using a shared scope.
Why ng-transclude's scope is not a child of its directive's scope if the directive has an isolated scope?
ng-transclude designed to allow directives to work with arbitrary content, and isolated scopes are designed to allow directives to encapsulate their data.
If ng-transclude didn't preserve scopes like that, any arbitrary content that you're transcluding would need to know the implementation details of your directive (i.e. it would need to know what's available on the isolated scope you created).
If it's not a bug, how can a container directive pass data to it's content, if not by setting attributes like I tried.
If the container directive and contained directives are coupled - i.e. you wrote both of them and need them to act together - then they should communicate via a shared controller.
If the container directive is supposed to inject content into the scope of the children (e.g. ng-repeat), then you shouldn't be using an isolated scope.
The angular documentation is quite clear on what the behaviour is supposed to be:
"In a typical setup the widget creates an isolate scope, but the
transclusion is not a child, but a sibling of the isolate scope. This
makes it possible for the widget to have private state, and the
transclusion to be bound to the parent (pre-isolate) scope."
you can manually transclude the child element
link: function(scope, element, attrs, ctrl, transclude) {
transclude(scope, function(clone, scope) {
element.find('.transclude-placeholder').append(clone);
});
}
The top answer is only correct for Angular up to v1.2.
Since Angular v1.3 the behaviour has changed, and it now behaves exactly as described in the "I expected" part of the question, making this question obsolete for Angular v1.3+.
Source: https://github.com/angular/angular.js/commit/fb0c77f0b66ed757a56af13f81b943419fdcbd7f