What is the lifecycle of a service (or factory) in angularjs and when is it re-initialzed ?
When Angular bootstraps, it attaches the constructor functions for services to the associated module. This happens once.
angular
.module('myApp')
.service('User', function UserConstructor() {
// stuff
});
When a you try to run a controller or something that depends on a certain service, Angular will inject it for you.
app.controller('FirstCtrl', function FirstCtrlConstructor(User) {
// User is available here
});
Under the hood, angular uses this thing called $injector to do dependency injection for you. It works something like this:
var $injector = {};
$injector.cached = [];
$injector.get = function(service) {
// check if service is in this.cached
// if so, return it
// if not
// 1) instantiate the service
// 2) store it in this.cached
// 3) return it
};
So when Angular sees that it needs to inject User into FirstCtrlConstructor, it calls $injector.get('User') to get the User. Since it hasn't injected User anywhere else before, it'll hit the "if not" condition and:
Call new User().
Store it in $injector.cached for next time.
Return it.
Now let's say that we need to inject User a second time:
app.controller('SecondCtrl', function SecondCtrlConstructor(User) {
// stuff
});
Again, when Angular sees that SecondCtrlConstructor depends on User, it calls $injector.get('User') to get the User so it could inject it. This time, it hits the "if so" condition. Since we previously put User in $injector.cached, it found it there for us. Thus, User doesn't get instantiated again.
Say we have a ThirdCtrl that also depends on User. It too would find it in $injector.cached, and so it wouldn't instantiate UserConstructor. Say that we have myDirective that depends on User. The same thing would happen - it'd find it in $injector.cached and thus wouldn't instantiate it.
This is called the Singleton pattern. It's used when you don't want to instantiate something more than once. Angular uses this for services so they don't get instantiated more than once. The same is true for factories (and probably also true for providers; probably not true for values and constants).
See https://medium.com/#adamzerner/dependency-injection-in-angular-18490a9a934 for some more info.
Services/factories are only initialized once, the first time they are used. From the docs: https://docs.angularjs.org/guide/services
Angular services are:
Lazily instantiated – Angular only instantiates a service when an
application component depends on it.
Singletons – Each component dependent on a service gets a reference to the single instance
generated by the service factory.
Related
I am building a single page app using angular and .net backend.
Aside from the individual restful resources that the controllers call, I also have a System Resource which provides the app with system wide information that is used/needed by most of my controllers.
I call the system service and store the data in the rootScope when the user first logs on.
However if for some reason the user refreshes the page which will lose the rootScope then the system data is lost and no longer available to the controller.
I have managed to get the System Service to trigger if a page reload happens BUT the problem is that in many cases the services running from the controller happen BEFORE the system service call which means that the data is not always available when the controller runs.
Whats the correct way to handle this in angular? Is there a way to make my controllers dependent on a certain rootScope state which if necessary will cause my system service API to be called before the controller makes its own service calls?
Thanks
One approach could be creating a factory/service which is injected to and called by every controller needing this information. This way you don't have to mess with $rootScope.
The request to get information should be cached so that you don't have to fire off a get everytime you switch controller. If you want to persist the information even after a page refresh you could use localstorage to store your data.
Example factory:
angular.module('appName')
.factory('Settings', ['$http', function ($http) {
function getData() {
var url = 'url/to/endpoint';
return $http.get(url, {cache: true});
}
return {
promise: getData()
};
}]);
By returning a promise from the factory we ensure that the getData() call is only run once. While in this particular instance it makes almost no difference (since it is returning an inner promise already), it is a pattern to follow for best practice.
This pattern also means that getData() is called on first use regardless of if the consuming controller accesses the promise. This allows for data to be exposed easily (data binding) without the need to use the promise in some use cases.
Used in controller:
angular.module('appName')
.controller('VeryNiceCtrl', ['$scope','Settings', function ($scope, Settings) {
Settings.promise.success(function(response){
var appSettings = response;
// do stuff
})
}])
I am new to angularJS and going through angular docs.I came across this line controllers are created using a factory function
I tried to find what that means and found what is factory and service and providers but that does not fit here.
How controllers are created using a factory function?
Please explain what is the meaning of factory in this context.
The key quote you are missing from the previous section you are referring to is this:
"First, there is a new JavaScript file that contains a so-called "controller". More exactly, the file contains a constructor function that creates the actual controller instance. "
If this were an actual angular factory, it would make more sense. But, controllers are instances just like Angular factories, services, and providers.
A factory, is actually a Javascript design pattern, maybe reading here it will make more sense.
For the controller to work, the instance must exist for the two-way binding to be able to take place. So basically, an instance of the controller is created. The angular controller page explains it well with:
"When a Controller is attached to the DOM via the ng-controller directive, Angular will instantiate a new Controller object, using the specified Controller's constructor function. A new child scope will be available as an injectable parameter to the Controller's constructor function as $scope." Here's the link.
In the event of controllers though, you would most likely store items on the $scope and not 'this'. So they separate controllers from factories this way as they do not return an accessible instance of themselves and instead bind their properties to the view through $scope or 'this'.
TO BE CLEAR, I'm not saying that they are referring to Angular factories. I believe the reason for this phrasing is tied to the same wording for the service factory function:
"Application developers are free to define their own services by registering the service's name and service factory function, with an Angular module.
The service factory function generates the single object or function that represents the service to the rest of the application. The object or function returned by the service is injected into any component (controller, service, filter or directive) that specifies a dependency on the service."
They give this example:
var myModule = angular.module('myModule', []);
myModule.factory('serviceId', function() {
var shinyNewServiceInstance;
// factory function body that constructs shinyNewServiceInstance
return shinyNewServiceInstance;
});
So when you see them say created from a factory function, they are just saying using the following pattern:
.controller('PersonCtrl', [
'PersonFactory'
function(PersonFactory) {
this.name = 'Tom';
console.log(PersonFactory.name); //would print 'Tom'
}]);
.factory("PersonFactory", [
function () {
return {
name: 'Tom'
};
}]);
I hope that helps, or maybe someone could be more concise in my explanation and edit this description.
What this means is that you provide AngularJS with a function that it can execute as many times as it needs to produce instances of your controller. So to take the example from the page you linked to:
angular.module('invoice3', ['finance3'])
.controller('InvoiceController', ['currencyConverter', function(currencyConverter) {
this.qty = 1;
this.cost = 2;
this.inCurr = 'EUR';
this.currencies = currencyConverter.currencies;
this.total = function total(outCurr) {
return currencyConverter.convert(this.qty * this.cost, this.inCurr, outCurr);
};
this.pay = function pay() {
window.alert("Thanks!");
};
}]);
That function that starts on line 2 with function(currencyConverter) { is the factory function.
Whenever the page has a location that uses an InvoiceController, AngularJS will (essentially) do the following with that factory function, passing in any dependencies that it has:
var currencyConverter = ...; // obtain a currency converter from the factory
var theController = new thatFactoryFunction(currencyConverter);
and then it will use the value that is returned as your controller. It will do this separately for each InvoiceController indicagted the page, creating a separate instance for each one.
(I stress that the code above is purely an illustration of what AngularJS is doing and not an actual representation of the code that it uses.)
The creation of a controller instance is interesting. One would expect that it is created with new InvoiceController(...), and it's also suggested by the sentence More exactly, the file contains a constructor function that creates the actual controller instance, but that's not the case. Actually it's created like this:
instance = Object.create(controllerPrototype);
and later the constructor function is called as function:
return fn.apply(self, args); //self == instance
To be honest, we can only guess what the author meant by factory function. It could be the fact that controllers are not created by new. Maybe the the constructor function is therefore referred to as factory or it's the internal factory function. It could also just be bad wording or even a mistake.
Hi i am having the 5 pages with 5 controllers and i am using the one service using injection in every controller . Is it possible to do without editing a code in views and controllers to stop the services functionality by writing code anywhere once in the any of one view or controller ?
For example I am having a app which uses the server to retrieve the data and i can writing a simple code to restrict that service instead of server i can access my localdata ?
All Angular services are application singletons. This means, you can change state of the service once, and the change will be 'visible' to all it's users.
The service gets instantiated when application will ask for it. While the service exists, all the controllers, etc. will receive a reference to the same service instance (I would still expect the service to be garbage collected when all the references to the service are lost).
This means, after you initialize the service, all controllers can invoke methods, etc. - all on the same instance, visible to all other instances.
Here's a jsFiddle showing the concept. When the value in factory object is changed in one controller, it is visible throughout the application. Both controllers use the modified value of testFactory.name, as it gets modified during the initialization of HelloCtrl. It is fairly easy to add two buttons that would count clicks in a field of factory's object and make both controllers display the value as it changes.
function HelloCtrl($scope, testFactory)
{
$scope.fromFactory = testFactory.sayHello();
testFactory.setName("ellitereit");
$scope.newValue = testFactory.sayHello();
}
function GoodbyeCtrl($scope, testFactory)
{
$scope.fromFactory = testFactory.sayGoodbye();
}
I'm trying to achieve a program structure like this:
The problem here is, when there is no apparent controller using the Features in the beginning, they are not instantiated and not registered in the FeatureRegistry, therefore they can't show up in the View. But what I would like is to achieve is that they show up in the view, then there template is loaded via ng-include and then in the template there are specific controllers for each feauture. These controllers are the ones that are using the Features.
The features are basically only there to tell about the location of templates and icons, which to use, and also to kick off the start of the Feature.
But back to my initial question:
How to instantiate the services even if they are not needed at the moment?
Or is there another function, that I can use for that instead of service? I would also like if you point me to that then :)
You can ask for it in the run part of your application, injector will invoke it.
angular.module("myApp", []).
factory("EagerService", function () {
console.log("I'm ready.");
}).
run(function (EagerService) {
console.log("EagerService is ready.");
});
Yet, as far as I understand, you have child/sub controllers that need this EagerService. Why don't you inject it there?
(Since this is relatively old - this answer is for future readers - but I stumbled across this question so maybe someone else will too) If you use providers/config blocks - they are done eagerly, so it's better to do eager initialization code there. You are/were probably thinking in terms of services/run blocks.
To demonstrate with code, this alert will not pop (assuming myServiceModule is a module that your application depends on and myService is not injected anywhere):
angular.module('myServiceModule', []).service('myService', function () {
alert("service");
// service
return {};
});
However this alert will pop even if no one is depending on the myProvider service:
angular.module('myProviderModule', []).provider('myProvider', function () {
alert("provider");
// Define your service here. Can be an array of inject-ables
// instead of a function.
this.$get = function () {
// service
return {};
};
});
You can see this in action in this plunker.
Read more about providers in the official documentation here.
Is it correct to pass the "current" $scope to an AngularJS service?
I'm in the situation where I've a $service knowing it's consumed by only one controller, and I'd like to have a reference to the controller's scope in the $service methods themselves.
Is this philosophically correct?
Or I'd better to broadcast events to the $rootScope and then make my controller listen to them?
To let the controller know when something async happens, use Angular promises.
To provoke the $apply, you don't need the scope, you can call $rootScope.$apply, as there is no difference calling it in a specific scope or in the root.
Regarding the variable reading, it would be better if you received parameters. But you could also read it from a scope as an object parameter, but I would go with parameter, that would make your service interface much more clearer.
I would say if your functionality is specific to one controller only than you don't need a service.
The controllers tasks is to manipulate the specific model whereas a service should deal with global tasks. I would rather stick to this paradigm instead of mixing things up.
This is what the docs say
Service
Angular services are singletons that carry out specific tasks common to web apps
Controller
In Angular, a controller is a JavaScript function(type/class) that is used to augment instances of angular Scope, excluding the root scope.
PS: Apart from that if you need to digest you can also inject the $rootScope within your service.
Yes. You can pass the $scope into the service when you initialize it. In the service constructor you can assign the scope to something like this._scope and then reference the scope within the service!
angular.module('blah').controller('BlahCtrl', function($scope, BlahService) {
$scope.someVar = 4;
$scope.blahService = new blahService($scope);
});
angular.module('blah').factory('blahService', function() {
//constructor
function blahService(scope) {
this._scope = scope;
this._someFunction()
}
//wherever you'd reference the scope
blahService.prototype._someFunction = function() {
this._scope['someVar'] = 5;
}
return blahService;
});
I personally believe that passing the whole $scope to a service is a bad idea, because it creates a kinda circular reference: the controller depends on the service and the service depends on the scope of the controller.
On top of being confusing in terms of relations, things like this one end up getting in the way of the garbage collector.
My preferred approach is to put a domain object in the controller scope and pass that to the service. This way the service works regardless whether it's used inside a controller or maybe inside another service in the future.
For example, if the service is supposed to push and pop elements from an array errors, my code will be:
var errors = [];
$scope.errors = errors;
$scope.myService = new MyService(errors);
The service interacts then with the controller by operating on errors.
Of course I've got to be cautious about never wiping out the whole array reference, but at the end of the day that's a general JS concern.
I'd never want to use broadcasting, $apply and/or similar things, because imho good OO-practices will always trump whatever Angular-magics.