There is a lot of reusable functionality that I have defined in my application that EVERY controller uses with the $scope variable. Instead of me having to create a shared service each time, is there a way to extend the $scope variable so that I can have my extended code available everywhere?
Something like:
//I've tested this out and it doesn't work, but this is what I want to do.
angular.module('App',[]).config(['$scopeProvider',function($scope) {
$scope.method1 = function() { ... };
$scope.method2 = function() { ... };
}]);
Then later on:
var HomeCtrl = function($scope) {
$scope.method1();
};
Is this possible? Or do I need to create a shared service and then have the $scope extend from that for the first line of each controller?
Instead of .config try .run, this will do exactly what you want.
angular.module('App', []).run(['$rootScope', function($rootScope) {
$rootScope.foo = function() {
alert("WIN!");
};
}]);
angular.module('App').controller('HomeCtr', ['$scope', function($scope) {
$scope.foo(); #will call the alert
}]);
NOTE I have only used module.controller because I like it, var HomeCtrl = function($scope) { will have the same effect.
Related
Suppose that I have a huge web application (that uses AngularJS) with a lot of controllers. Is there a way to inject $log service in every controller? To be more clear, I want to write something like this:
.config(function($log) {
allMyControllers.inject($log);
})
instead of
.controller('Controller1', function($log) {...})
.controller('Controller2', function($log) {...})
.controller('Controller3', function($log) {...})
.controller('Controller4', function($log) {...})
Possible thing that you can do is, create a controller that has all needed dependencies and make it as base controller and other controllers can extend it using angular extend api.
some clear example code which I came accross :
.controller('baseController', function(someService) {
this.someService = someService;
})
.controller('extendedController', function($scope, $controller) {
angular.extend(this, $controller('baseController', { $scope: $scope }));
this.alert = this.someService.alert;
})
.service('someService', function() {
this.alert = function() {
window.alert('alert some service');
};
});
Working solution of above code can be found here.
I have a google material dialog box where I interact with the user. I want to inject a factory into the dialog and use it there. Is there a way to do this?
I tried passing the factory as a local variable.
Any suggestions?
Edit ---------
Here is my controller that I am using to handle the dialogs. I output the factory to the console and the functions are all there. Not sure why calling them does nothing.
function DocumentDialogController($mdDialog, locals, chartFactory)
{
var vm = vm || this;
vm.thisItem = locals.thisItem;
vm.cancel = cancel;
vm.acceptChanges = acceptChanges;
vm.selectImage = selectImage;
function cancel(){
$mdDialog.cancel();
};
function acceptChanges(desc) {
$mdDialog.hide(desc);
};
function selectImage(imageName) {
vm.thisItem.imageObj.data.src=imageName;
};
}
Assign a controller to the dialog and inject the factory into the controller.
Do something like the below to create the dialog. Note that the string in the controller property is the name of the controller you want to use.
$mdDialog.show({
templateUrl: 'partials/example.template.html',
targetEvent: event,
controller: 'ExampleCtrl',
controllerAs: 'vm'
});
Create and inject the factory into the controller like you would with any other controller.
(function() {
'use strict';
angular
.module('example')
.controller('ExampleCtrl', ExampleCtrl);
function ExampleCtrl(ExampleFactory) {
var vm = this;
// you can now use the injected ExampleFactory
}
ExampleCtrl.$inject = ['ExampleFactory'];
})();
I'm trying to figure out how controller inheritance works. I have three controllers:
var myApp = angular.module('app', []);
myApp.controller('MainController', ['$scope', function($scope) {
$scope.name = 'main';
$scope.getName = function() {
return $scope.name;
};
}]);
myApp.controller('Child1', ['$scope', function($scope) {
$scope.name = 'child1';
}]);
myApp.controller('Child2', ['$scope', function($scope) {
$scope.name = 'child2';
}]);
and my view
<div ng-app='app'>
<div ng-controller='MainController'>
<div ng-bind='getName()'></div>
<div ng-controller='Child1'>
<div ng-bind='getName()'></div>
<div ng-controller='Child2'>
<div ng-bind='getName()'></div>
</div>
</div>
</div>
</div>
but they're all showing "main". How do I fix this?
here's a fiddle http://jsfiddle.net/g3xzh4ov/3/
Here's an example of how controllers can be extended in Angular.
myApp.service('baseCtrl', function () {
this.name = 'base';
this.getName = function() {
return this.name;
};
});
myApp.controller('MainController', ['baseCtrl', function (baseCtrl) {
angular.extend(this, baseCtrl);
this.name = 'main';
}]);
myApp.controller('Child1', ['baseCtrl', function (baseCtrl) {
angular.extend(this, baseCtrl);
this.name = 'child1';
}]);
myApp.controller('Child2', ['baseCtrl', function (baseCtrl) {
angular.extend(this, baseCtrl);
this.name = 'child2';
}]);
It obliges to use controllerAs, which replaces $scope with this, it is especially good for such cases.
Notice the usage of service instead of other Angular service types, it uses new under the hood, so this... statements can be brought right from a controller to separate service.
There are several ways of doing controller inheritance. Here is another approach.
Regarding the original code, there is no 'controller iheritance' in Angular. And $scope prototypical inheritance assumes that
$scope.getName = function() {
return $scope.name;
};
returns $scope.name from the context where it was defined, it is MainController function in your case.
The problem that you're facing is actually based on core Javascript functionality.
You see, the confusion that you're facing stems from the mix of scoping, and prototypical inheritance. The properties are copied over, but the scoping remains the same, preventing you from accessing the variables that you expect to be able to access. To understand this better, perhaps instead of $scope, we can look at a simpler variable:
myApp.controller('MainController', ['$scope', function($scope) {
var a = 1;
$scope.getName = function() {
console.log(a); // -> 1
console.log(b); // Error! `Uncaught ReferenceError: b is not defined`
};
}]);
myApp.controller('Child1', ['$scope', function($scope) {
var b = 2;
}]);
Obviously, MainController doesn't know that Child1 defined some variable called b, so it errors. That variable is strictly out of lexical scope.
Likewise, if we renamed b to a, it won't turn the value in MainController to 2. This demonstrates exactly what's happening for you: you have three things called $scope, and only one is in the lexical scope.
Two options to fix it:
1) use this:
$scope.getName = function() {
return this.name;
};
The this solution works because of how Javascript determines "this" based on context. Basically, since it's attached to a given $scope Object, that Object's a good candidate. But Mozilla can explain this better than I can, so view their page on the topic here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
2) Otherwise, you can simply pass the $scope:
$scope.getName = function(item) {
return item.name;
};
If you want to overwrite name property in child scopes ,convert your primitive name property into object.
$scope.user = {};
$scope.user.name='main';
$scope.getName = function() {
return $scope.user.name;
};
And you should read https://github.com/angular/angular.js/wiki/Understanding-Scopes for detailed information.
I have written a service in AngularJS, but I can't get it to work with the angular-seed way of doing things.
The controller code is as follows:
/*function PhotoCtrl($scope, Photo) {
$scope.photos = Photo.query();
}*/
angular.module('myApp.controllers', []).
controller('PhotoCtrl', [function($scope,Photo) {
$scope.photos = Photo.query();
}])
.controller('MyCtrl2', [function() {
}]);
Note that the commented out section works fine, but I would like to handle it somewhat like the (recommended) second way.
The error I get is that Photo is undefined, so my guess would be my method of passing (injecting) it is wrong, but I can't find out how to do it correctly
You need to define the Photo service:
angular.module('myApp.controllers', [])
.service('Photo', ['$log', function ($log) {
return {
query: function() {
// the query code here.
}
};
}])
.controller('PhotoCtrl', ['$scope', 'Photo', function ($scope, Photo) {
$scope.photos = Photo.query();
}])
.controller('MyCtrl2', [function() {
}]);
A couple of references:
http://docs.angularjs.org/api/angular.Module
http://docs.angularjs.org/api/AUTO.$provide#service
In the above sample code I used parameters aliasing, which I suggest in order to avoid issues when minifying your code.
See also an example here:
AngularJS multiple uses of Controller and rootScope
And a Plunker here:
http://plnkr.co/edit/Bzjruq
I'm trying to use the angular-seed template with the default settings. In controllers.js I use
angular.module('myApp.controllers', []).
controller('MyCtrl1', [function($scope) {
$scope.test = 'scope found!';
}])
.controller('MyCtrl2', [function() {
}]);
There the $scope is always undefined.
When I take the controller out of the module and register it globally it works fine. As here:
function MyCtrl1($scope) {
$scope.test = "scope found!";
}
MyCtrl1.$inject = ['$scope'];
Could someone explain to me why this is?
You cannot mix things like that. You need to decide on one of the two possibilities:
app = angular.module('test', []);
// possibility 1 - this is not safe for minification because changing the name
// of $scope will break Angular's dependency injection
app.controller('MyController1', function($scope) {
// ...
});
// possibility 2 - safe for minification, uses 'sc' as an alias for $scope
app.controller('MyController1', ['$scope', function(sc) {
// ...
}]);
I would not advise using the other syntax which declares Controller directly. Sooner or later with the growth of you app it will become hard to maintain and keep track. But if you must, there are 3 possibilities:
function myController1 = function($scope) {
// not safe for minification
}
function myController2 = ['$scope', function(sc) {
// safe for minification, you could even rename scope
}]
var myController3 = function(sc) {
// safe for minification, but might be hard
// to read if controller code gets longer
}
myController3.$inject = ['$scope'];
This is the proper way:
angular.module('myApp.controllers', []);
angular.module('myApp.controllers').controller('MyCtrl1', ['$scope', function($scope) {
}]);
I was also searching for that one, it seems that you need to type '$scope' before the function, as below:
angular.module('myApp.controllers', []).
controller('MyCtrl1', ['$scope', function($scope) {
$scope.test = 'scope found!';
}])
.controller('MyCtrl2', ['$scope',function() {
}]);
It kinda makes sense, I think it should be more clear though..
You can simply remove '[' and ']' when You are using $scope.
angular.module('myApp.controllers', []).
controller('MyCtrl1', function($scope) {
$scope.test = 'scope found!';
})
.controller('MyCtrl2', [
function() {
}
]);