Angular, Why does the child controller have same scope as parent? - angularjs

I have created a test located on plunker at this address: Full example on plunker
Here is the child controller which works:
child.directive('child', function(){
return{
restrict:'E',
templateUrl: 'child.html',
link: function(){},
controller:['$scope', function($scope){
this.inherited = $scope.test;
this.local = "child";
}],
controllerAs:'child'
};
});
I am expecting the controller located in child.js to be a child controller of the controller in script.js. This would mean that if I want to access a variable added to the scope of parent controller from the child controller, I would need to access it using $scope.$parent. Can someone explain why these are the same scope?

By default, directives do not create a new scope. So in your example $scope in the directive will be the same value as the scope that the controller is contained in. The scope is not inherited - they are the same scope.
So for this markup:
<div ng-controller="SomeController">
<child></child>
</div>
scope in SomeController will be the exact same object as scope in your directive. If you did $scope.$parent in the directive, you would actually be accessing the parent scope of SomeController. This is equivalent to
scope: false //default for directives
If you add:
scope: true
to your directive definition, then the directive will create a new scope that prototypically inherits from the parent scope. In this situation,
<div ng-controller="SomeController">
<child></child>
</div>
Now $scope.$parent in the directive will be the $scope of SomeController. Also, since it prototypically inherits, the directive will have access to methods and properties defined on $scope.$parent, with the caveats and complications that come with prototypically inheritance in javascript.
If you use:
scope: {}
Now $scope.$parent in the directive will still be the $scope of SomeController, but the new scope will not prototypically inherit from the parent, so you will not have access to methods and properties defined on $scope.$parent.

The child scope inherits from the parent scope, so anything you define in the parent will appear in the child scope. Keep in mind that this is a new scope, so modifications won't affect the parent directly without using the $scope.$parent scope. Take a look at this question and the accepted answer: AngularJS access parent scope from child controller

In directives the parent scope is inherited by default:
https://github.com/angular/angular.js/wiki/Understanding-Scopes

Related

AngularJS - decouple directive from parent controllerAs syntax

I have a child directive inside a parent directive. I want the child directive to be completely generic and not have to rely on the naming of the parent directive's controllerAs syntax. To explain myself better here is what i Have
Parent Directive
templateUrl: '/apps/common/myParentDirective.html',
controller: 'myParentDirectiveController',
controllerAs: 'vm'
Child Directive (this has to inherit parent scope and watch a property named 'dynamicFields' on the scope of the parent directive. Right now i have the code below
$scope.$watch('vm.dynamicFields', function (newVal) {
if (!newVal) {
return;
}
// do something with dynamicfield
});
I want this directive to be re-usable and don't want to be tied into using vm.dynamicFields. Can i simply use 'dynamicFields' as it is guaranteed that the name of the field on the scope will always be the same but it not guaraneteed that the parent directive would be using 'vm' or 'somethingelse'

"controller as" with isolate scope in directives not isolating

I'm trying to learn to build custom directives in AngularJS. Presently I am using AngularJS 1.5.8.
I am trying to create an example of a directive with an isolate scope where the controller scope is not visible, so that I can selectively expose things by adding them to 'scope: {}.' In this example, what I expect to get is 'Name: Street:' because ctrl.customer should be unavailable to the directive. Then later I would add "customer: '='" to "scope: {}" and I would get "Name:David Street:123 anywhere street". Unfortunately, I am getting "Name:David Street:123 anywhere street" from this directive as it is written.
things I have tried:
setting "bindToController: true"
removing "scope: {}" and setting "bindToController: {}" (so I add "customer: '='" to that)
setting "bindToController: false" (might as well, right?)
repeatedly hitting shift-F5 after loading the html so make sure that I just don't have the old file cached.
testScope.js:
var app = angular.module('scopeModule',[]);
app.controller('Controller',[function(){
var vm = this;
vm.customer = {
name: 'David',
street: '123 anywhere street'
};
}]);
app.directive('sharedScope', function() {
return{
scope:{},
template: 'Name:{{ctrl.customer.name}} Street:{{ctrl.customer.street}}',
controller: 'Controller',
controllerAs: 'ctrl',
bindToController: true
};
});
index2.html:
<!doctype html>
<html ng-app="scopeModule">
<body>
<shared-scope></shared-scope>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<script src="testScope.js"></script>
</body>
</html>
Controller is the controller of sharedScope directive. It is a single entity. this in directive's controller is ctrl in directive's template, because both controller and template belong to a directive.
There's no reason to isolate directive's scope from itself.
Isolated scope is isolated from the scopes of other directives. From the reference:
{...} (an object hash): A new "isolate" scope is created for the
directive's element. The 'isolate' scope differs from normal scope in
that it does not prototypically inherit from its parent scope. This is
useful when creating reusable components, which should not
accidentally read or modify data in the parent scope.
This means that if sharedScope directive has no controller and parent directive has Controller controller, this
<div ng-controller="Controller as ctrl">
<shared-scope></shared-scope>
</div>
will result in
Name: Street:
output.

Access parent controller in angular directive

having same nested directives:
<div mydir="a">
<div mydir="b">
</div>
</div>
if mydir requires ?^mydir it always get itself's controller.
test_plunk
is it possible to access parent's controller?
According the angular documentation on jqLite, you can call controller() to retrieve the controller for any given element:
controller(name) - retrieves the controller of the current element or its parent.
By default retrieves controller associated with the ngController directive.
If name is provided as camelCase directive name, then the controller for this
directive will be retrieved (e.g. 'ngModel').
Within your link function, you can retrieve the parent controller by calling controller() on the parent element and passing in the name of the directive:
var parentCtrl = iElement.parent().controller('mydir');
$scope.$parent.a;
shall give you a
$scope is the scope injected by angular
$parent is a keyword reference to the parent scope from any scope in angular.
Yes this is JavaScript, and as was asked in the question, AngularJS powered Javascript

angular ui modal can NOT refer to parent scope

i am using angular ui modal to create modal in my project.
Everything works fine until I need to refer to variable in parent scope. see plunker code
It seems like modal can't access parent scope. Is there anyway to overcome this?
Angular UI's modals use $rootScope by default (See the documentation here).
You can pass a scope parameter with a custom scope when you open the modal – e.g. scope: $scope if you want to pass the parent scope. The modal controller will create a sub-scope from that scope, so you will only be able to use it for your initial values.
You'll need to refer to the parent scope in your $modal options. Angular documentation
Corrected Plunker Version
Below is also a code snippet of what I added to make it work.
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl,
scope:$scope, //Refer to parent scope here
resolve: {
items: function () {
return $scope.items;
}
}
});
You can add an ID to the parent div and use his scope.
<div id="outerdiv" ng-controller="OuterCtrl">
<h2>Outer Controller</h2>
<input type="text" ng-model="checkBind">
<p>Value Of checkbind: {{checkBind}}</p>
And set up a "fake" binding within the controller
//init
$scope.checkBind = angular.element(document.getElementById('outerdiv')).scope().checkBind;
$scope.$watch('checkBind', function (newValue, oldValue) {
//update parent
angular.element(document.getElementById('outerdiv')).scope().checkBind = $scope.checkBind;
});
See http://plnkr.co/edit/u6DuoHJmOctFLFhvqCME?p=preview

AngularJS: when creating a new directive, why there is a controller?

To my understanding, controller is responsible for preparing the model, and pass the model to the directive which is responsible for updating DOM.
So when creating a new directive, why there is a controller inside?
Does this mean that I can do something like connecting server inside a directive?
app.directive('hover', function () {
return {
restrict: 'E',
controller: function ($scope) {
// what is the controller for?
...
}
}
}
});
From the docs on $compile service:
controller
[…] The controller is instantiated before
the pre-linking phase and it is shared with other directives (see
require attribute). This allows the directives to communicate with
each other and augment each other's behavior. The controller is
injectable (and supports bracket notation) with the following locals:
$scope - Current scope associated with the element
$element - Current element
$attrs - Current attributes object for the element
$transclude - A transclude linking function pre-bound to the correct transclusion scope. The scope can be overridden by an optional first argument.
function([scope], cloneLinkingFn).

Resources