Accessing inherited scope with Controller As approach - angularjs

With the original way to define controllers, accessing the parent's scope was fairly trivial, since the child scope prototypically inherits from its parent.
app.controller("parentCtrl", function($scope){
$scope.name = "Parent";
})
.controller("childCtrl", function($scope){
$scope.childName = "child of " + $scope.name;
});
<div ng-controller="parentCtrl">
{{name}}
<div ng-controller="childCtrl">
{{childName}}
</div>
</div>
The Controller-As approach seems to be the recommended way to declare a controller. But with Controller-As, the above approach no longer works.
Sure, I can access the parent scope with pc.name from the View:
<div ng-controller="parentCtrl as pc">
{{pc.name}}
<div ng-controller="childCtrl as cc">
{{cc.childName}}
</div>
</div>
I do have some issues with this (potential for spaghetti code), but this question is about accessing the parent scope from the child controller.
The only way I can see this working is:
app.controller("parentCtrl", function(){
this.name = "parent";
})
.controller("childCtrl", function($scope){
$scope.pc.name = "child of " + $scope.name;
// or
$scope.$parent.pc.name = "child of " + $scope.name;
// there's no $scope.name
// and no $scope.$parent.name
});
So now, the child controller needs to know about "pc" - except, this should (in my mind) be restricted to the view. I don't think a child controller should know about the fact that a view decided to declare a ng-controller="parentCtrl as pc".
Q: What's the right approach then?
EDIT:
Clarification: I'm not looking to inherit a parent controller. I am looking to inherit/change the shared scope. So, if I was to amend the first example, I should be able to do the following:
app.controller("parentCtrl", function($scope){
$scope.someObj = {prop: "not set"};
})
.controller("childCtrl", function($scope){
$scope.someObj.prop = "changed";
});

After researching, I came to the following realization:
Controller-As approach is NOT a substitute for using $scope. Both have their place, and can/should be used together judiciously.
$scope does exactly what the name implies: i.e. it defines ViewModel properties on the $scope. This works best for sharing scope with nested controllers that can use the $scope to drive their own logic or to change it.
Controler-As defines the entire controller object as a ViewModel with a named scope (via the controller's alias). This works best only in the View (but not other controllers), if the View decides if it wants to reference a specific controller ViewModel.
Here's an example:
var app = angular.module('myApp', []);
// Then the controllers could choose whether they want to modify the inherited scope or not:
app.controller("ParentCtrl", function($scope) {
this.prop1 = {
v: "prop1 from ParentCtrl"
};
$scope.prop1 = {
v: "defined on the scope by ParentCtrl"
};
})
.controller("Child1Ctrl", function($scope) {})
.controller("Child2Ctrl", function($scope) {
// here, I don't know about the "pc" alias
this.myProp = $scope.prop1.v + ", and changed by Child2Ctrl";
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="ParentCtrl as pc">
<div ng-controller="Child1Ctrl">
<div>I know about the "pc" alias: {{pc.prop1.v}}</div>
</div>
<div ng-controller="Child2Ctrl as ch2">
<div>I only care about my own ViewModel: {{ch2.myProp}}</div>
</div>
</div>

You should do like :
html
<div ng-controller="ChildController as child">
<button type="button" ng-click="child.sayMe()">Say me!</button>
</div>
js
var app = angular.module('myApp', [])
app.controller('BaseController',function() {
this.me = 'Base';
this.sayMe= function() {
alert(this.me);
}
});
app.controller('ChildController', function($scope, $controller) {
var controller = $controller('BaseController as base', {$scope: $scope});
angular.extend(this, controller);
this.me = 'Child';
});
take a look at https://docs.angularjs.org/guide/controller

For anyone looking to simply access the parent scope programmatically, use the $scope service, find the parent scope, and access the name used for the parent scope's controllerAs, e.g.:
$scope.$parent.someName.doSomething();

Related

Angularjs: How to build a controller reuseable?

I have an application with some controllers that have the same functions as: InitMenu, GetData, Search, Paging, ..
How can I build a generic controller with the main functions: InitMenu, GetData, Search, Paging, .. without having to write in each specific controller?
share all the common logic inside a service and inject it inside your controllers.
.service('CommonData, function(){
this.getData = function(){
}
this.InitMenu= function(){
}
this.search= function(){
}
})
otherwise you can make a parent controller and all the child controllers will prototype inherit from the parent scope.
.controller('ParentCtrl', function($scope){
$scope.myObj = {};
$scope.parentMethod = function(){
return myObj;
}
})
.controller('ChildCtrl', function($scope){
var stuff = $scope.parentMethod();
//you can access the parent method in the child template as well
})
<div ng-controller="ParentCtrl">
<div ng-controller="ChildCtrl">
{{parentMethod()}}
//if you use controllAs syntax change it to {{parentCtrlName.parentMethod()}}
</div>
</div>
if you use controllerAs syntax you can access the parent $scope by specifyng the controller name as a scope field in the child controller. e.g $scope.parentCtrlName.parentMethod() and parentCtrlname.parentMethod() in the child template.

Programmatically creating new instances of a controller

So here is my problem, I have some functions/variables in a parent controller
function parentController($scope) {
$scope.numberOfChildren = $scope.numberOfChildren + 1 || 1;
console.log($scope.numberOfChildren);
$scope.someFunction = function(argument) {
// do stuff
$scope.someVariable = result of the function
}
}
I am calling this controller in two other controllers that are directives controllers and are called in the same view
function firstChildController ($scope, $controller) {
var aVariable = 1;
$scope.otherVariable = 10;
$controller('parentController', {$scope: $scope});
$scope.someFunction(aVariable);
}
function secondChildController ($scope, $controller) {
var aVariable = 6;
$scope.otherVariable = 11;
$controller('parentController', {$scope: $scope});
$scope.someFunction(aVariable);
}
What I want, is not to share the parent scope for the two children.
Right now, there is only one instance of the parent controller and so when I call two directives depending on it on the same view, I get $scope.numberOfChildren === 2.
What I want is this parent controller to be loaded twice but have separated scopes ($scope.numberOfChildren === 1 in each child controller)
I managed to do this using ng-controller in the view template and deleting the $controller calls but I want to do it programmatically. (I don't want to have to write the same ng-controller code each time I am calling the directive).
<div ng-controller="parentController">
<first-directive></first-directive>
</div>
<div ng-controller="parentController">
<second-directive></second-directive>
</div>
Finally, to keep homogeneity in the code of the project, I'd rather not use the this and vm stuff to do the job if it possible.
parentController does NOT have its own scope, it operates on the $scope you're passing to it when you instantiate it this way $controller('parentController', {$scope: $scope}).
Checkout this simple demo fiddle.
The problem in your case might be caused by directives sharing the same scope and, thus, passing the same scope to the parent controller.
What you expect is exactly same with the way system run: scope is not sharing between two controller.
When you use a ng-controller in html, a new scope (controller instance) will be created. From your code above, two controller instance will be created. You can see it by adding {{$id}} to html and see id of scope instance.
<div ng-controller="parentController">
{{$id}}
<first-directive></first-directive>
</div>
<div ng-controller="parentController">
{{$id}}
<second-directive></second-directive>
</div>
If you see {{numberOfChildren == 2}} mean that your code is wrong in somewhere, not by sharing scope issue.

AngularJS inherit a single scope from another controller

Is there a way to inherit a single scope from one controller to another controller via $controller?
Here is my current code:
var peopleApp = angular.module('peopleApp', []);
peopleApp.controller('indexController', function($scope){
$scope.sampleVar = "Hello World";
$scope.sampleVar2 = "Hello World Again";
});
peopleApp.controller('addController', function($scope, $controller){
$controller('indexController', {$scope.sampleVar: $scope.sampleVar});
alert($scope.sampleVar);
});
I know this will throw an error because of "$controller('indexController', {$scope.sampleVar: $scope.sampleVar});" that should be "$controller('indexController', {$scope: $scope});" but the concept or desired return is there.
<body ng-controller="parentController">
<div ng-controller="childController1">
</div>
<div ng-controller="childController2">
</div>
</body>
You can inherit properties form parent controller to childController1 and childController2
Angularjs internally maintaines scope hierarchy via ng-controller directive where you defined in dom. For More detials in https://docs.angularjs.org/guide/controller
Another way is Inject $rootScope inside controller and access properties from
anywhere in your app controllers
var app = angular.module('sample',[])
app.controller('controller1',function($rootScope,$scope){
$rootScope.parent ="I am visible in any controller";
})
app.controller('controller2',function($rootScope,$scope){
//here you can access rootScope property using rootScope or Scope
// Because rootScope is parent for all scope object
// Angular scope protochain is like javascript protochain it looks from child to parent scope
console.log( $rootScope.parent);
console.log($scope.parent);
})

AngularJS: ng-app inside ng-include

I have a template like this.
<body ng-app="demo" ng-controller="demo">
<div ng-include="/main.html">
</div>
</body>
And the main.html is.
<div ng-app="main" ng-controller="main>
""
</div>
here is the js.
JS-1
var myapp = angular.module('demo', []);
myapp.controller('demo', function($scope,$routeParams, $route,$http) {
$scope.variable="444"
})
JS-2
var mainapp = angular.module('mainapp', []);
myapp.controller('main', function($scope,$routeParams, $route,$http) {
})
Is it possible to access the scope of JS-1 inside JS-2?, if yes how, if no is there any solution to this.Thanks.
It depend what you want to do.
If you want read $scope.variable variable from JS-1, you should see it in JS-2 $scope.
If you want modify $scope.variable form JS-1, you should create method in JS-1:
$scope.changes = function(data){
$scope.variable = data;
}
This method also should be available in JS-2 $scope.
This isn't nice solution but should work.
The best solution is to create service which will provide operations on JS-1 fields.

AngularJS Modules/Scope Sharing

I recently started using AngularJS and the way I'm building my apps now is like this:
MainController.js
var app = angular.module('app', ['SomeController', 'MainController']);
app.controller('MainController', function ($scope) {
// do some stuff
}
SomeController.js
var SomeController= angular.module('SomeController', []);
SomeController.controller('SomeController', function ($scope) {
$scope.variable = "test";
// do some otherstuff
}
The problem that Im' running into is that the scope is not being shared between modules. From MainController I can't get the variable "test" for example.
What is the best practice for this? Do I store all my controllers in 1 module in 1 file?
How can i have 1 page with 2 controllers and share the $scope between them, or is it OK to put everything in just one controller ?
You could use a service like this: Live demo here (click).
JavaScript:
var otherApp = angular.module('otherApp', []);
otherApp.factory('myService', function() {
var myService = {
someData: ''
};
return myService;
});
otherApp.controller('otherCtrl', function($scope, myService) {
$scope.shared = myService;
});
var app = angular.module('myApp', ['otherApp']);
app.controller('myCtrl', function($scope, myService) {
$scope.shared = myService;
});
Markup:
<div ng-controller="otherCtrl">
<input ng-model="shared.someData" placeholder="Type here..">
</div>
<div ng-controller="myCtrl">
{{shared.someData}}
</div>
Here's a nice article on sharing data with services.
You can also nest controllers to have the parent controller's scope properties inherited by the child scope: http://jsbin.com/AgAYIVE/3/edit
<div ng-controller="ctrl1">
<span>ctrl1:</span>
<input ng-model="foo" placeholder="Type here..">
<div ng-controller="ctrl2">
<span>ctrl2:</span>
{{foo}}
</div>
</div>
But, the child won't update the parent - only the parent's properties update the child.
You would use "the dot rule" to have updates on the child affect the parent. That means nesting your properties in an object. Since the parent and child both have the same object, changes on that object will be reflected in both places. That's just how object references work. A lot of people consider it best practice to not use inheritance, but put everything in directives with isolated scope.
You can use $rootScope, each Angular application has exactly one root scope.
Reference
app.controller('MainController', function ($scope, $rootScope) {
$rootScope.data = 'App scope data';
}

Resources