so in the AngularJS docs I saw this about the injector:
// You write functions such as this one.
function doSomething(serviceA, serviceB) {
// do something here.
}
// Angular provides the injector for your application
var $injector = ...;
///////////////////////////////////////////////
// the old-school way of getting dependencies.
var serviceA = $injector.get('serviceA');
var serviceB = $injector.get('serviceB');
// now call the function
doSomething(serviceA, serviceB);
///////////////////////////////////////////////
// the cool way of getting dependencies.
// the $injector will supply the arguments to the function automatically
$injector.invoke(doSomething); // This is how the framework calls your functions
it looks nice. but i don't get it. in the last line where the injector looks for dependencies, isn't it exactly like having global variables serviceA, serviceB? I mean, say I would rewrite it like this:
var serviceA, serviceB;
function doSomething() {
// access serviceA, serviceB
}
what is the benefit of having the injector do that? I mean, if he can magically find the right objects for the arguments, doesn't it mean I can find them just as easily if they are global variables?
I hope my question is clear...
BTW, if you're doing something that genuinely needs to be at a lower level than $scope, Angular has you covered:
http://docs.angularjs.org/api/ng.$rootScope
The docs for $rootScope are a little light, but generally speaking, you inject it and use it the same as regular $scope. It is the shared root scope of the app you're in.
The only reason to use Window scope would be to communicate between multiple Angular apps. And even then...you got it, Angular has you covered:
http://docs.angularjs.org/api/ng.$window
Related
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.
I was wondering when would you use $injector.get('someService') vs injecting that service directly.
Basically, what is the difference of the two code below?
angular
.module('myApp')
.controller('MyController', MyController);
/** Difference of this **/
MyController.$inject = ['$rootScope', '$route']; // and more services
MyController($rootScope, $route)
{
/* ... */
}
/** And this **/
MyController.$inject = ['$injector'];
MyController($injector)
{
var $rootScope = $injector.get('$rootScope');
var $route = $injector.get('$route');
/* and so on */
}
If you are not sure what your controller needs, or that you will update it in the future, and you have it refactored, then using $injector it is a lot easier to add/remove dependencies.
The first approach uses dependency injection while the second uses service locator pattern. Dependency injection has the following advantages:
It makes a component's dependencies explicit. For example, you can look at the constructor function of a controller and immediately know what dependencies it needs. With service locator, you usually have no idea because the controller might invoke the service locator any time.
It makes unit test easier. For example you can use the $controller service to instantiate a controller and feed it with mock objects via the locals argument. That's certainly easier than having to define a bunch of AngularJS services or factories so that $injector can resolve them. It becomes worse when coupled with #1: you might not be aware of all dependencies a component needs in order to supply all the necessary mocks.
Service locator does offer some flexibility thought. For example, your code might use a service only if it exists, like this:
if ($injector.has('serviceA')) $injector.get('serviceA').doSomething()
else someSomethingElse()
With dependency injection, if serviceA doesn't exist by the time your component is constructed, you'll get an error.
As to ease of refactoring, I don't think it's difficult to add/remove parameters to the constructor function. As pointed out in a comment to your question, tools like ngAnnotate will help make the declaration DRYer.
Therefore, I would just stick with dependency injection and only use service locator when absolutely necessary.
I find that I am using $scope before all variables and functions that I have which are defined in one place and then used in my application. For example:
var app = angular
.module('app',
....
...
.run(['$rootScope', '$angularCacheFactory',
function ($rootScope, $angularCacheFactory) {
$scope.one-hour-cache = $angularCacheFactory('1-hour', {
maxAge: 3600000,
cacheFlushInterval: 3600000,
deleteOnExpire: 'aggressive',
storageMode: 'localStorage'
});
How can I make so I don't always use $scope to reference every variable like this?
Would it be reasonable to have some globals so that my application is not full of $scope..... =
If so then where should I define the variables and how ?
To improve the communication in your App you could use events, and If you'd want to dig into more, you could take a look to the observer and mediator patterns.
http://jorshasaur.us/simple-observer-pattern-in-angular/
Anyway, if you'd want to use a "global variable" you must add it to the $rootScope
You should encapsulate those data or whatever struct it is into a service and have it injected in your controllers. Having it injected is going to pay off in the future while writing your tests.
I've been reading through the tutorial, dev guide, and practicing on my own, but I'm having trouble piecing everything together in my mind with regard to dependency injection.
Question: Within the first code snippet in the linked page below, why is the name of the "service" located in front of $inject and why is the parameter of the service used here again? Or better yet what concepts am I lacking in understanding? I'd like to be able to piece it all together in my head step by step, but I'm still trying to understand how exactly even the globally defined "services/functions" can be written this way.
http://docs.angularjs.org/guide/dev_guide.services.understanding_services
So in that code snippet is injecting the $location service into MyController. So MyController depends on $location so it declares the dependency and its owns the dependency declaration.
Here is the code commented:
// declaring a Controller function
var MyController = function($location) { ... };
// $location service is required by MyController
MyController.$inject = ['$location'];
// Then register the Controller in the module.
// The module is the container that performs DI on the objects within it.
myModule.controller('MyController', MyController);
Typically though you'd do the following to declare the Controller and it's dependencies in one shot that's cleaner. The dependencies are strings at the front of the array before the final function which is the controller being registered. Here is the simpler definition:
myModule.controller('MyController', ['$scope', '$location', function($scope, $location) {
$scope.someFunction = function() {
// do something with $location service in here
};
}]);
Keep in mind this:
"...even the globally defined "services/functions"
The whole point of DI is to not define things globally because global definitions create coupling that make it hard to reuse major portions of your system (ie you can't break apart the system without instantiating the whole thing). Dependency Injection separates the dependency (ie MyController depends on/uses $location service) from where it finds that reference. Beginner developers, and some dense senior devs quite frankly, typically just define things globally and that's how everything gets a reference to their dependencies. DI allows code to simply declare its dependencies so its dependencies can be given to the code by an external entity instead of the code assuming where to get it from. Often this is called the Hollywood Principle - Don't call us we'll call you.
It looks like you're lacking a strong understanding of Dependency Injection in AngularJS.
Once you define a service, it then needs to be injected inside to the controller that is going to use it. The two code samples on that page show the two different methods of injecting the service into the controller.
I'd suggest you look at the docs: AngularJS: Dependency Injection
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.