I have following example and i want to execute the content of the scope function before returning it but it doesnt
directives.directive('itemElement', function() {
return {
restrict : 'E',
scope : function(scope) {
var itemModelVar = getValue(scope.item.testdata, field.caption);
console.log('test');// this is not executed at all for example
return {
item : '=item',
field : '=field',
schematypes : '=schematypes',
itemmodel : itemModelVar
};
},
templateUrl : 'partials/templates/item-simple.html'
};
});
This leads to itemModelVar being undefined.
AngularJS directive definition does not allow this kind of scope definition. Scope within directives can be assigned in 3 ways.
scope: false
scope: true
scope: {}
In the first scenario, the scope within the directive is the same as the parent scope.
In the second scenario, the parent scope is prototypically inherited in the directive scope.
In the third scenario, the scope is defined is known as isolate scope since here the scope is not inherited but a new identifier which is specific to the directive. Isolate scopes are used for reusable components.
Try and understand the behavior of scope in angularjs.
Related
"use strict";
angular.module("foo")
.directive("breadcrumbs", function($rootScope, $timeout) {
return {
replace: true,
scope: false,
templateUrl: '/js/shared/directives/breadcrumbs.tpl.html',
link: function(scope, element, attrs, controller, transcludeFn){
console.debug(scope.parent);
...
Sometimes scope.parent is the scope of the current controller, sometimes it's $rootScope. I've discovered that when my custom directive is used like this:
<breadcrumbs></breadcrumbs>
it receives the controller scope, but when used with ng-if:
<breadcrumbs ng-if="searchData"></breadcrumbs>
it receives a new scope nested within the controller scope.
How do I write my directive to always receive the controller scope without resorting to any hackery (such as adding ng-if="true")?
Update: I've corrected the description of the problem. The issue is that I get scopes at different levels of nesting depending on whether ng-if is used, regardless of whether scope: false or scope: true was used.
First of all, if the directive is in context of a controller scope, you'll never receive $rootScope as the scope of directive.
When you use ng-if, you are receiving a prototypically inherited child scope (of the immediate parent scope, in this case the controller) created by ng-if becuase your directive has scope: false (which means pass in the closest scope instead of creating one)
You can always request a prototypically inherited child scope by setting scope:true or an isolated scope via scope:{}.
What ng-if=true dose is create a new scope for your element.
You could achive that by changing the parameter
From
scope: false
To
scope: true
I'm applying my directive (attribute) to 2 of my modals that are on the same file.
my directive :
.directive('myModal', function() {
return {
scope : true,
restrict: 'A',
link: function(scope, element, attr) {
console.log(scope.dismiss);
scope.modalElements.push(element);
scope.dismiss = function(){
angular.forEach(modalElements,function(element,key){
console.log(element);
// element.modal('hide');
});
};
console.log(scope.dismiss);
$(element).bind('shown.bs.modal', function () {
if (!scope.$root.$$phase){
scope.$apply();
console.log(scope.dismiss); // the function I want
console.log(scope.$parent.dismiss); // the function with alert
}
});
}
};
});
and I have this scope at my controller (so it means the child scope of rootscope which is also the parent of parent of parent of the scope created inside the directive).
$scope.dismiss = function() {alert(1);};
As you can see in my directive, I'm updating the function dismiss.
It works for the current level of scope but it doesn't update the parent scope or the prototyping hierarchy.
Why is that hapening ? should I go through scope.$parent.$parent.$parent ?
Yes, you've exhaustively demonstrated in the example how prototype chain works. The inheritance works only in one direction. To overcome this behaviour of prototype inheritance you should define objects on the scope, not functions. E.g.
scope.obj = {};
scope.obj.dismiss = function() { ... };
This way scope.obj.dismiss will be the same in both child and parent scopes, unless scope.obj is re-assigned.
Using functions or data from parent controllers in directives either this way or with scope.$parent isn't a good practice in general. Directive's require, services and $broadcast/$emit are more appropriate ways of interoperation within Angular.
I can't set a scope variable inside my custom directive's controller.
See this plkr:
http://plnkr.co/edit/KVGVxhgRHxkhCLytkLBv?p=preview
Link function prints the variable name but interpolator does not evaluate {{name}}. I set it in the controller of my custom directive with isolate scope. Still for some reason, scope variable does not persist. Is this behavior expected? If so what is the proper way to set scope variables?
Help is appreciated.
Import 'name' from your outside scope into your isolated scope using '=' binding:
// Code goes here
angular.module('test', [])
.directive('nametag', function() {
return {
scope: { name: '='},
controller: function($scope, $attrs, $log) {
$scope.name = 'John Doe';
},
link: function(scope, elem, attrs) {
// elem.text(scope.name);
}
};
});
HTML:
<div ng-app="app" ng-init="name='james'>
<nametag name="name"></nametag>
</div>
When you define an isolated scope for your directive (by specifying scope: {}) you create a private scope that does not prototypically inherit from the parent scope. You can think of an isolated scope as your directive's private sandbox.
Since your directive's scope is isolated, you need to import scope variables from parent scope into your isolated scope through attribute bindings. In this case we are establishing two-way binding between the parent scope variable 'name', and the directive's isolated scope variable with the same name.
Your directive controller assigns the name to the isolated scope of the directive. So you have two possibilities:
Remove the scope: {} from your directive to not create an isolated scope. While this works it is maybe not what you want as your directive modifies the outer scope.
Add a template to your diective containing the <h1>Hello {{name}}</h1>. See this plunker.
Using scope: { ... } in a directive introduces an isolate scope, which does not prototypically inherit from its parent scope. But I have always used it for a different reason: a convenient way to declare HTML attributes with two way data binding:
scope: {
attr1: '=',
attr2: '?='
}
To get a non-isolate scope, you have to use scope: true, which does not offer the opportunity to declare such attributes. I now find myself needing a directive with a non-isolate scope, but with two way binding. What's the best way to achieve this?
Example: My use-case is something like this, in the view of the outer-directive:
<div ng-repeat="e in element">
<inner-directive two-way-attr="e.value"></inner-directive>
</div>
But inner-directive is in the same module as outer-directive. It doesn't need to be encapsulated with an isolate scope. In fact, I need to use $scope inheritance for other purposes, so an isolate scope is not an option. It's just that using an HTML attribute to establish this two-way communication is extremely convenient.
The answer by pixelbits helped me figure this out big time, but taken as a direct answer to my original question, it seems overly complicated. After looking into it, the solution is really quite simple.
Take a directive with an isolate scope like this:
scope: { model: '=myModel' },
link: function(scope, element, attr) {
//...
}
The following is equivalent, except that the scope is not isolate:
scope: true,
link: function(scope, element, attr) {
scope.model = scope.$parent.$eval(attr.myModel);
//...
}
See a working example here: http://jsfiddle.net/mhelvens/SZ55R/1/
Working Demo Here
It is possible to have both a non-isolate scope and an isolate scope in the same directive. You might want to do this, for example, if you have a mix of both non-isolated templates (meaning they should not look for bindings through scope inheritance), and isolated templates (they should look for bindings in its own scope only) and they are both defined in the same directive.
In order to setup both isolate scope and non-isolate scope, you can do the following:
In your directive definition, specify scope=true
In your link function, compile and link your template against the scope parameter. When you do this, the bindings are evaluated against the non-isolate scope (meaning it resolves bindings through prototypical scope inheritance).
link: function(scope, element, attr) {
// this template should look for 'model' using scope inheritance
var template2 = angular.element('<div> Prototypical Scope: {{ model }}</div>');
// add the template to the DOM
element.append(template2);
// compile and link the template against the prototypical scope
$compile(template2)(scope);
}
The advantage of prototypical scope inheritance is that you don't have to explicitly import bindings into your directives' current scope. As long as it is defined in the current scope or any scope higher up the inheritance chain (all the way up to the root scope), the angular run-time will be able to resolve it.
In the same link function, define an isolated scope using scope.$new(true). You can establish a two-way binding of your model by importing a model into your isolated scope - isolatedScope.model = scope.$eval(attr.model):
link: function(scope, element, attr) {
// this template should look for 'model' in the current isolated scope only
var template = angular.element('<div>Isolate Scope: {{model}}</div>');
// create an isolate scope
var isolatedScope = scope.$new(true);
// import the model from the parent scope into your isolated scope. This establishes the two-way binding.
isolatedScope.model = scope.$eval(attr.model);
// add the template to the DOM
element.append(template);
// compile and link the template against the isolate scope
$compile(template)(isolatedScope);
}
The advantage of the isolate scope is that any bindings that exist (ie. are in-scope) are the ones that you explicitly import. Contrast this with non-isolate scope - where the bindings do not need to be explicitly defined on the current scope - it could be inherited from any scope higher up the chain.
I wrote this. You use it like this:
twowaybinder.attach($scope, $attrs.isDeactivated, 'isDeactivated');
.factory('twowaybinder', function ($parse) {
function twoWayBind($scope, remote, local){
var remoteSetter = $parse(remote).assign;
var localSetter = $parse(local).assign;
$scope.$parent.$watch(remote, function (value) {
localSetter($scope, value);
});
$scope.$watch(local, function (value) {
remoteSetter($scope, value);
});
}
return {
attach : twoWayBind
};
});
It will give u true two-way binding from scope values. Note I dont think that $scope.$parent is neccessary, as in an inherited or no scope scenario any expression should resolve on the current scope. You would only need to call $parent in an isolated scope in which case you wouldn't use this, you would use the isolated scope config.
you may use two directives
if gg is an object then "=" points to one place of memory!
angular.module('mymodule', []).directive('a', function($parse, $modal) {
return {
restrict : 'A',
scope : {
gg : "="
},
require : "b",
link : function(scope, element, attrs, bCtrl) {
scope.$watch('gg',function(gg){
bCtrl.setset(scope.gg);
}
}
}
});
angular.module('mymodule').directive('b', function($parse, $modal) {
return {
restrict : 'A',
/*
* scope : { showWarn : "=" },
*/
controller : function($scope) {
$scope.bb = {};
this.setset = function(nn) {
$scope.bb=nn;
};
}
});
Here is my Code:
// HTML
<body>
<h1>{{foo.name}}</h1>
<my-directive></my-directive>
</body>
// Scripts
app.directive('myDirective', function() {
return {
restrict: 'E',
replace: true,
scope: true, //**********
template: '<h4>{{foo.name}}</h4>',
controllerAs: 'foo',
controller: fooCtrl,
link: function(scope) {
console.log(scope);
}
}
});
var fooCtrl = function() {
this.name = 'FOO';
}
My Question:
If I use controllerAs syntax and don't set scope: true in myDirective, the controller will become global, so the controller will insert foo.name into Tag. But once I set the scope as true, the controller will only apply on myDirective.
How could this happened? Does a controller inside directive create a new scope that inherits from the surrounding scope? Why it will apply on global?
UPDATE
This is a very silly question, as I always use isolate scope before, so forget about the usage of scope inside directive, read the docs and clearly understand. Thanks for answer
I guess your are asking regarding the scope property in Angular Directives. Also I presume you mean $rootScope by term 'global'. As explained in this guide, In a directive, scope property behaves as follows,
scope.false
The default option which does not create a new scope for a directive
but shares the scope with its parent
scope.true
Creates a new scope but prototypically inherits from the parent scope.
scope: ‘isolate’
Creates an isolated scope which does not prototypically inherit from
the parent scope but you can access parent scope using scope.$parent
Check out the directive documentation and scroll down a bit to the part about the scope option. Setting scope to true creates a new scope that the controller is attached to. Not setting scope defaults it to false, which causes the directive to use the parent scope. That's why the controller is being attached to the parent scope.