What is the default for AngularJS directive scope? - angularjs

What is the default scope value of an AngularJS directive?
Of course, it is not isolated scope. It is true or false.
I can't find any documentation on what it is.

"Note, by default, directives do not create new scope -- i.e., the default is scope: false"
from Understanding scopes.
Using the scope option in directive you can:
create a child scope prototypically inherited with scope: true
create an isolated scope with scope: {} then you can bind some property to parent scopes with '#', '&', '=' (see this question).
decide to not create a new scope and use parent with scope: false (default).

By default, directives shared the scope of the containing controller.
You can specify scope:true to have a inherited scope and to get a isolated scope you have to do the following
scope:{
something1:"#",
something2:"="
something3:"&"
}

Related

Priority between custom and built-in directve

I am reading ng-book-r27.
There are some something i can't understand well.
About 'Scope Option' of The chapter 'Directives Explained'.
First confusion:
If multiple directives on an element provide an isolate scope, only
one new scope is applied. Root elements within the template of a
directive always get a new scope; thus, for those objects, scope is
set to true by default.
I think that mean other directives will use the isolate scope as theirs.
is that right?
Second confusion:
example of inherited scope
ng-init has higher priority than custom directive.
Why the expression of ng-init will use the scope of custom dirctive.
I find a recommendation from offical doc about ng-init:
This directive can be abused to add unnecessary amounts of logic into
your templates. There are only a few appropriate uses of ngInit, such
as for aliasing special properties of ngRepeat, as seen in the demo
below; and for injecting data via server side scripting. Besides these
few cases, you should use controllers rather than ngInit to initialize
values on a scope.
OK, I can ignore the strange behavior of second confusion.
I have not read the book ng-book, but as far as I know, the statement of your first confusion does not conform with the AngularJS documentation regarding inherited and isolated scopes within a directive.
The statement above is simply not possible, having multiple directives that each have their own isolated scope would produce a $compile:multidir error. Here is a DEMO.
.directive('elem1', function($rootScope) {
return {
restrict: 'A',
scope: {}
};
})
.directive('elem2', function() {
return {
restrict: 'A',
scope: {}
}
});
After scanning the AngularJS documentation, there is no supporting statement that validates the statement:
If multiple directives on an element provide an isolate scope, only
one new scope is applied.
The closest statement I see that is similar to the statement above is the scope: true option definition when creating directives:
true: A new child scope that prototypically inherits from its parent
will be created for the directive's element. If multiple directives on
the same element request a new scope, only one new scope is created.
The new scope rule does not apply for the root of the template since
the root of the template always gets a new scope.
The statement above suggests that when multiple directives with scope: true option (not an isolated scope) resides in a single element, it would create one scope and everything else is a shared scope. DEMO
.directive('elem1', function($rootScope) {
return {
restrict: 'A',
scope: true,
link: function(scope) {
console.log(scope.hello);
}
};
})
.directive('elem2', function() {
return {
restrict: 'A',
scope: true,
link: function(scope) {
scope.hello = 'world';
console.log(scope.hello);
}
}
});
You would notice that both directives logs 'world', this obviously supports the statement above.
If you read more in the $compile scope AngularJS documentation, you would see that:
In general it's possible to apply more than one directive to one
element, but there might be limitations depending on the type of scope
required by the directives. The following points will help explain
these limitations. For simplicity only two directives are taken into
account, but it is also applicable for several directives:
no scope + no scope => Two directives which don't require their own scope will use their parent's scope
child scope + no scope => Both directives will share one single child scope
child scope + child scope => Both directives will share one single child scope
isolated scope + no scope => The isolated directive will use it's own created isolated scope. The other directive will use its parent's scope
isolated scope + child scope => Won't work! Only one scope can be related to one element. Therefore these directives cannot be applied to the same element.
isolated scope + isolated scope => Won't work! Only one scope can be related to one element. Therefore these directives cannot be applied to the same element.
Perhaps you're having problems identifying the difference between prototypically inherted scopes and isolated scopes. You might want to read the $rootScope.Scope $new() method, the isolate parameter definition.
First question:
I think that mean other directives will use the isolate scope as
theirs. is that right?
The answer is an absolute no, in reference towards multiple directives with isolated scope, it would produce the $copile:multidir error.
For your second question:
ng-init has higher priority than custom directive. Why the expression
of ng-init will use the scope of custom directive?
As for directives that do not have isolated scopes or directives that don't prototypically inherit from their parent scope, you can directly associate those directives as having a scope property definition with a falsey scope value:
falsy: No scope will be created for the directive. The directive will
use its parent's scope
If a directive is bound to an element with a scope of its own then it simply uses the scope of that element, otherwise it seeks all the scope instances within the scope chain until it reaches the $rootScope.

Behaviour bindToController in child scope versus isolated scope

I was playing around with the bindToController option for directives. I stumbled upon a seemingly strange difference between the behaviour using a child scope compared to an isolated scope. When I use an isolated scope, an new scope is created for the directive, but changes to the bound controller attributes are forwarded to the parent scope. Yet when I use a child scope instead, my example breaks. (Using bindToController using child scopes should be allowed according to http://blog.thoughtram.io/angularjs/2015/01/02/exploring-angular-1.3-bindToController.html#improvements-in-14 )
The code:
{
restrict: 'E',
scope: {},
controller: 'FooDirCtrl',
controllerAs: 'vm',
bindToController: {
name: '='
},
template: '<div><input ng-model="vm.name"></div>'
};
Working demo https://jsfiddle.net/tthtznn2/
The version using a child scope:
{
restrict: 'E',
scope: true,
controller: 'FooDirCtrl',
controllerAs: 'vm',
bindToController: {
name: '='
},
template: '<div><input ng-model="vm.name"></div>'
};
Demo: http://jsfiddle.net/ydLd1e00/
The changes to name are forwarded to the child scope, but not to the parent scope. This in contrast to binding to an isolated scope. Why is this?
This is because you are using the same alias for both controllers (and has nothing to do with object vs string value as mentioned in the comments).
As you might know, the controller as alias syntax is merely creating an alias property on scope and sets it to the controller instance. Since you are using vm as the alias in both cases (MainCtrl and FooDirCtrl) you are "shadowing" the MainCtrl alias in the case of the normal child scope.
(In this context, "normal" means "prototypally inheriting from parent scope".)
Thus, when you are trying to evaluate vm.name (vm for MainCtrl) on the new scope to get the "parent value", it is actually evaluating FooDirCtrl.name (which is undefined) and the same happens when you are trying to assign back to the parent scope.
The isolate scope version is not affected, since the scope is not inheriting from it's parent scope.
Updated fiddle
UPDATE:
Taking a closer look at the source code, this might be a bug (because support for bindToController on non-isolate scopes was added "retroactively").
We seem to have been getting away with this bug, because of the prototypal inheritance, but when the names collide we are out of luck.
I have to take a closer look to make sure if it's indeed a bug and if it's "fixable", but for now you can work around it by using different aliases for your controllers (see fiddle above).
That's (part of) why I don't like using vm as my conroller alias (despite it being suggested by popular style-guides) :)
Let's track this in angular.js#13021.

Angular JS - isolate scope bind to attribute or parent's attribute

I have two custom directives, one is a child of the other. For simplicity let's call them 'outer' and 'inner'. The inner requires the outer and has an isolate scope like this.
require:'^outer',
restrict: 'EA',
scope: {
disabled: '#'
},
The outer also has an isolate scope and also has an attribute binding to 'disabled'
What I am trying to do inside the inner's link function is check to see if the value of disabled is true, if it is not set then I would like to use the outer directive's disabled value which I will default to false.
Should this just be automatic, e.g. the attribute binding will bind to the attribute OR the parent scope if the attribute is not supplied, or do I have to use something like $parent to access outer's scope?
If you set scope: true then your inner directive will get a scope that's prototypically inherited from its parent, so that should give you access to the properties on the parent scope.

Why use scope in directive and how to use it?

I see so many examples https://docs.angularjs.org/guide/directive and What is the difference between & vs # and = in angularJS but even I don't understand the scope principe in a directive. It's very confusing to use this. Some examples make use of scope:true; and
scope: {
sourceObj: '=',
lookupSource: '#',
searchRes: '&',
disableSearch: ''
}
What is using a boolean value (scope:true;) doing exactly?
scope:true
If you set scope:true (instead of scope: { ... }) then prototypical inheritance will be used for that directive.
This is not something AngularJS is doing – this is how JavaScript prototypal inheritance works.
example
scope:false (default)
the directive does not create a new scope, so there is no inheritance here. This is easy, but also dangerous because, e.g., a directive might think it is creating a new property on the scope, when in fact it is clobbering an existing property. This is not a good choice for writing directives that are intended as reusable components.
example
scope: {......}
the directive creates a new isolate/isolated scope. It does not prototypically inherit. This is usually your best choice when creating reusable components, since the directive cannot accidentally read or modify the parent scope.
Notice, even the parent scope has a name “Harry”, the textbox inside directive is blank. This is because of the new Isolated scope doesn’t know anything about its parent scope.
example
Notice, even the parent scope has a name “Harry”, the textbox inside directive is blank. This is because of the new Isolated scope doesn’t know anything about its parent scope.
NOTE: I dont want now post diferences between scope properties (#,&,=) , it will be another lesson
from this source
It's all about whether or not you want to create an isolated (new) scope for your directive or you want to inherit the from the parent scope. Specifying scope: true is essentially the same as scope: {}. It just says, 'hey, I want my own private scope here'. The only difference between those two is that you can use the object notation to specify your own scope properties to use in your directive.

Can I replace scope for isolated scope?

I have directive in which I have scope mentioned as :
return {
restrict: 'AE',
transclude: true,
scope: {
model: '=',
menu: '='
},}
so my question is apart from model and menu variables what all variables are accessible to me? Does Isolated scope inherits parent scope variables ?
If I don't want to use isolated scope then what could be done where I can set these two variables in directives and inherits all variables from parent scope ?
Thank you.
If I understood correctly, what you want to achieve is a new scope inheriting all the parent stuff. That is easy to do.
By default a directive uses the parent scope, but you can do two different things:
scope: {} // Isolated scope
or:
scope: true // new scope inheriting from parent
So with the later, you will have all the parent scope has but you can set new stuff there and the parent won't know. Example:
http://plnkr.co/edit/oL5ALPvkEzkiXSuOSNnE?p=preview
I hope this is what you asked for.
EDIT: I edited the plunker. The idea with new scopes that inherits from others is:
If parent has a primitive like name the child will inherit it, but if you do something like:
childScope.name = ".." you are not modifying the parent name, you are shadowing it, AKA creating a new name that will hide the parent name. this means that the parent will never know if the child modified the name.
foo is something created in the new child scope, that means that the parent will never know.
user is not a primitive, it is an object and when the child modifies its name, it is not shadowing the entire user, it is just modifying its value:
childScope.user.name = "Fox"
This is getting the user reference and modifying its value, is not modifying the entire user, so you are not shadowing it. If you do:
childScope.user = { name: 'Foo' };
Then you're creating an entire user, AKA you're creating a new reference so that will shadow the parent user and the parent-child relationship of that object will end.
This is what we call the dot rule.
If the scope property of the directive definition is set to an { /* object hash */ } an isolated scope is created for your directive. If you set the scope property to true a new scope is generated that inherits from the parent scope.
See this plunker for a demo on directive scope behaviour.
If you don't want to isolate the scope but do want to pass argument to the directive you can use the $observe method of the attributes service.
function linkingFn(scope, elm, attrs, ctrl) {
// get the attribute value
console.log(attrs.ngModel);
// change the attribute
attrs.$set('ngModel', 'new value');
// observe changes to interpolated attribute
attrs.$observe('ngModel', function(value) {
console.log('ngModel has changed value to ' + value);
});
}
Creating a two way binding without an isolated scope can be done with the $parse service.
when you set scope like this, it's an isolated scope, and as its name says, it doesn't inherit any property from parent scope.
However, you can directly define variable on the scope, in the directive link method, like this:
.directive('someDirective', function(){
return {
restrict: 'C',
link: function(scope, element, attrs){
scope.newVariable = "newVariable"
}
}
}
it adds newVariable to your scope.Hope it helps~
More info on directive can be seen here, http://docs.angularjs.org/guide/directive. There is a section with the name Creating a Directive that Manipulates the DOM

Resources