Reputation: 16282
i just come across a sample code on Angular js Controllers inheritance from this url http://viralpatel.net/blogs/angularjs-controller-tutorial/
here is the code and i just do not understand one part.
<div ng-controller="BMWController">
My name is {{ name }} and I am a {{ type }}
<button ng-click="clickme()">Click Me</button>
</div>
<script>
function CarController($scope) {
$scope.name = 'Car';
$scope.type = 'Car';
$scope.clickme = function() {
alert('This is parent controller "CarController" calling');
}
}
function BMWController($scope, $injector) {
$injector.invoke(CarController, this, {$scope: $scope});
$scope.name = 'BMW';
}
</script>
1) i just do not understand this line of code $injector.invoke(CarController, this, {$scope: $scope});
2) which $scope is BMWController scope and which $scope is CarController scope ?
3) there is two scope {$scope: $scope} one in left and one in right which one is related with BMWController & CarController scope ?
4) why this keyword is used in invoke function ?
5) please help me to understand this line $injector.invoke(CarController, this, {$scope: $scope}); as much with easy explanation
thanks
Upvotes: 1
Views: 132
Reputation: 2150
After reading the Doc, I think that these line means that you are:
$injector.invoke(CarController...
: invoking the CarController function;... , this ...
: a reference to the invoker;...{$scope: $scope})
: the CarController has a dependency of $scope, so this line means that you are injecting th BmwController $scope into the CarController.Besides all this, I really think that controller inheritance is a realy bad practice. If you need to share some logic between two controllers you should use Angular Services. They are singletons that exists during all lifecycle of your application and are meant to store logic or data, and, more importantly, make your controllers skin and elegant.
Take a look at this example:
https://jsfiddle.net/relferreira/2b5amcya/
HTML:
<div data-ng-app="app">
<div data-ng-controller="MainController as mainVm">
{{mainVm.name}}
</div>
<div data-ng-controller="DetailController as detailVm">
{{detailVm.name}}
{{detailVm.other}}
</div>
</div>
JS:
angular.module('app', []);
angular.module('app')
.controller('MainController', mainController);
mainController.$inject = ['UserService'];
function mainController(UserService){
var vm = this;
vm.name = UserService.getName();
}
angular.module('app')
.controller('DetailController', detailController);
detailController.$inject = ['UserService'];
function detailController(UserService){
var vm = this;
vm.name = UserService.getName();
vm.other = 'test';
}
angular.module('app')
.factory('UserService', userService);
function userService(){
var name = 'Renan';
return{
getName: getName
}
function getName(){
return name;
}
}
EDIT:
Looking at the AngularJs source code we can find the invoke method:
function invoke(fn, self, locals, serviceName) {
if (typeof locals === 'string') {
serviceName = locals;
locals = null;
}
var args = injectionArgs(fn, locals, serviceName);
if (isArray(fn)) {
fn = fn[fn.length - 1];
}
if (!isClass(fn)) {
// http://jsperf.com/angularjs-invoke-apply-vs-switch
// #5388
return fn.apply(self, args);
} else {
args.unshift(null);
return new (Function.prototype.bind.apply(fn, args))();
}
}
As you can see, it is using the Function.prototype.apply() method. As stated by Alex, this method needs a context and it is most used by inheritance and/or override methods. Take a look at this answer:
https://stackoverflow.com/a/560952/1108979
it uses call
method, that is similar to the apply
.
Upvotes: 1
Reputation: 1264
The line $injector.invoke(CarController, this, {$scope: $scope});
calls the function CarController
and passes the BMWController
's $scope
as a parameter.
You can see it like:
function CarController($scope) {
// ...
}
function BMWController($scope, $injector) {
CarController($scope);
$scope.name = 'BMW';
}
and you can also pass other parameters like:
function CarController($scope, foo) {
// ...
}
function BMWController($scope, $injector) {
// ...
$injector.invoke(CarController, this, {$scope: $scope, foo: bar});
}
So, instead of calling the controller function directly, you use the dependency injector to call it. The dependency injection is a design pattern that has multiple advantages as explained here.
Edit
this
an optional parameter to the invoke function $injector.invoke(fn, [self], [locals]);
and is used to pass the current context: BMWController
to the invoked controller function CarController
.
In this example we define this.foo
on the BMWController
context and then we can access foo
on the CarController
context.
function CarController($http, $scope) {
console.log(this.foo) // prints: bar
// ....
}
function BMWController($scope, $injector) {
this.foo = "bar";
$injector.invoke(CarController, this, { $scope: $scope });
// ....
}
Upvotes: 2
Reputation: 4290
1) you run a function (CarController
) and provide it arguments ($scope
)
2) You pass your current scope (of BMWController
) to the CarController
, so they share scopes.
3) the line {$scope: $scope}
creates an object with the key $scope
and initializes it with the object $scope
. Try to look at the first $scope
just like any other object key, like name
would be in {name: 'Mou'}
4) Invoke just runs the function with the given arguments: $injector documentation
You pass a function to the invoke function, but that function is without context. You need to tell it the where you want to run it. So by passing this which is the BMWController it runs it kind of inside the BMWController
Upvotes: 2