I want to inject instances of a class. From this thread it looks like services return new serviceArgFunction, which is what I want. https://groups.google.com/forum/#!msg/angular/56sdORWEoqg/VxECXKbn3gsJ
But I can't seem to get it to work. For some reason, my two instances of share state. Can anyone help me figure out what I'm doing wrong?
http://jsfiddle.net/kTjMy/5/
var Klass = function() {
this.count = 0;
this.getAndIncCount = function() {
this.count += 1;
return this.count;
};
};
app.service('service', Klass);
You can use a factory that returns the constructor for your class:
app.factory('myKlass', function() {
return Klass
});
Even more simply, if Klass is defined elsewhere, you can use value:
app.value('myKlass', Klass);
Either way, inject it as normal.
function CtrlA($scope, myKlass)
{
new myKlass();
}
See this jsFiddle.
[Edit] See also this Google Groups post and this associated example.
To make it clear I created a little Plunker.
It has a Service and a Factory.
Service and Factory have a value and setters and getters.
Both are injected into Controllers A and B.
If You change the value of the Service in Controller A, this change takes also place in Controller B, because the service is a Singleton.
But when You change the value in the Factory in the Controller B, this affects only B because the change takes only place in the instance that is created in B.
Enjoy!
Maximilian - thank you for the great example.
I converted this to JSFiddle, as a place to experiment with another design pattern that I wanted to dig into.
Why is it that swapping the provider type from "service" to "factory" still seems to work?
It seems more about how the provider is created i.e. using 'new' versus just referencing it directly.
Also, within the service, is there a design pattern as to why a service should implement the "this" keyword, as opposed to using using return { myAccessor : function () { return val; } }
See
http://jsfiddle.net/jeffsteinmetz/XF69p/
In this jsfiddle, you can change one line of the code (line 23), and it still works, the line of code can be either:
App.service('MyService', function() {
or
App.factory('MyService', function() {
Why do these both work?
It seems more about how you reference the service with "new"
var myInstance = new MyFactory();
Which is different than just calling it directly
MyService.getVal()
Any angular folks care to lay out the best practices and reasoning behind this:
when to use 'new' and when you don't need new
why use the 'this' reference approach to define your provider instead of return { accessorName : function}
why do both factory or service work in the above example?
Related
I have gone through lots of document and also refereed stack overflow post regarding this issue.
I am still having confusion When to use Service and Factory.
Can any one Explain using Real World Example when to use what ?
Common thing about service & factory is they are singleton objects,
there instance gets created once per application.
Basically angular service does return an object by using new keyword, whether factory does return an object which you have created.
Service
app.service('myService', function(){
this.myMethod = function(){
return 'something';
};
})
In above service we added one method myMethod which can be available to the the component whoever inject the service. Service does gives access to all of its properties which are assigned to its this context.
Factory
app.factory('myService', function(){
//here you can do some initial work before creating an service object.
//which is very advantageous part of service.
var configuredVariable = 'configured';
return {
myMethod : function(){
return 'something'+ configuredVariable;
};
}
})
You could have controller over object before creating it, you could do some configuration kind of setting before returning an object from a service.
More detailed answer here
Summary
When to Use service and When to factory in agularJS?
Used service whenever you don't won't to return an configurable object, If you want to configure the object you should go for an factory.
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'm getting re-acquainted with angular after a long time away, and (so far) I've discovered two ways to create an angular service. The simplest being this:
var app = angular.Module("SomeApp", [])
.factory("SomeService", function ($log) {
$log.info("Yea beh beh! Dis hea is a service!")
});
This form of creating a service in angular is documented in angular.Module.factory. But, you can also see, that on the same page there is another way to create a service, using angular.Module.service.
Reading the two descriptions, I am unable to understand the differences other than .service needs you to explicitly use new to instantiate a service, whereas .factory implicitly does it for you. I might be wrong here, since I'm unable to understand because I have no clue what a $get property is. So, to wrap up:
What is a $get property?
What is the difference between .service and .factory?
Lastly, because this bugs me:
With all angular.Module.{service, factory, controller}, the second argument is a function. But, for instance, you have put in a list for the second argument in a controller, and name its dependencies. Then why is the type taken to be a function, rather than object? I mean you won't know from the documentation that you can declare dependencies in a list unless you've done a tutorial or something.
Both angular.Module.factory and angular.Module.service are proxies to angular.Module.provide. When you give a function to angular.Module.factory angular creates the respective service by invoking the function and using its return value as the service. On the other hand, when you give a function to angular.Module.service angular will treat it as an constructor and invoke it with the new keyword to create the service.
Thus creating a service with angular.Module.factory looks like this:
app.factory('MySimpleService', function () {
return {
aServiceProperty : 'a property containing a string'
};
});
And creating a similar service with angular.Module.service looks like this:
app.service('MyOtherSimpleService', function () {
this.aServiceProperty = 'a property containing a string';
});
The definition of the $get propery can be found here
Looks like this question has already been asked and answered. I am unable to comment, so I posted an answer.
So, this isn't the typical question of HOW to do it. I know it can be done with a service or a factory but I was wondering if someone could share what the advantages/disadvantages would be of just creating a basic service, injecting it into each controller, and then extending the service with functions from each controller. Something similar to the following example..
app.service('HelperService', function() {
return {};
});
app.controller('Controller1', function($scope, HelperService) {
$scope.somefunc = function() {
//do stuff here
};
HelperService.somefunc = $scope.somefunc;
});
app.controller('Controller2', function($scope, HelperService) {
HelperService.somefunc();
});
This works, and works well. I feel a bit stupid for asking this but it just seems like I'm missing something here as to why this isn't used or recommended?
It may work, but its a bad idea.
Controller2 HelperService.somefunc() won't exist until Controller1 has been instantiated. So theres an implicit dependency of Controller2 on Controller1
the code on HelperService isn't in one place where it can be understood together
if you are doing some sort of data manipulation in that function, it really should be operating on data encapsulated by the HelperService.
The service is a singleton and it will be instantiated once calling new on the function itself -- the function you pass in is essentially a constructor. This will create the empty object you are returning for use everywhere, but if you want to return an object in such a way it makes more sense to use .factory, but this is not a big deal.
At any rate, you can consider your code to conceptually do this:
var HelperService = function () {}
var helperService = new HelperService;
function Controller1() {
helperService.someFunc = function () {}
}
function Controller2() {
helperService.someFunc();
}
I would consider this a dangerous thing to do for a couple of reasons:
Controller1 must be instantiated before Controller2 or else somefunc won't be available to Controller2. Ideally the controllers would have no knowledge of each other.
You are coupling Controller/ViewModel (since you're using scope) with service level logic, but these should be decoupled. HelperService shouldn't know about the controllers either. Instead, you should be injecting a service that has an API that the controllers expect to use. This doesn't always have to be HelperService, it just has to look like HelperService to the controllers and its API shouldn't change.
Without knowing specifics about what you're trying to do, it's hard to advise. In general you may rethink what you want to do, but you can extend functionality of services with other services. Consider services to be in their own layer.
There seems to be no way to provide data to an Angular controller other than through attributes in the DOM handled by directives (of which ngInit is a handy example).
I'd like to provide other "constructor" data, e.g. objects with functions to my
$scope.
Background: We have an existing dashboard-style single page application,
where each widget manages a <div>, and widget-instance-specific data
is provided as an object along with support functions, etc.. This object data
doesn't fit nicely into DOM attributes or ngInit calls.
I can't really come up with a better way to it than to have a global hash, and use an instance-specific unique key. Before calling angular.bootstrap(domElement, ['myApp']), we set up all "constructor" parameters in this global hash under the key and then use
<div ng-init='readInitialValuesFromHash("uniqueKey")'>...</div>
where readInitialValuesFromHash gets all its data from
globalHash["uniqueKey"] and stores what it needs it in $scope (possibly
just the "uniqueKey").
(What seems like an alternative is to use a directive and jQuery.data(), but jQuery.data uses a global hash behind the scenes)
Of course I can hide the global data in a function, but fundamentally still use
a singleton/global variable. This "global hash and pass key as param to ng init
trick" just seems like such a hack...
Am I missing something? Is there a better way, given that the
widget-instance-specific data is actually more complicated than suitable for
inserting in the DOM via directives/attributes due to the legacy dashboard
framework?
Are there dangers when putting complicated objects in the $scope as long as they aren't referenced by directives, {{}} or $scope.$watch() calls?
Angular's front page says:
Add as much or as little of AngularJS to an existing page as you like
So in light of that, how should I proceed?
EDIT: Comments have asked to make my question more clear. As an example of a non-trivial constructor parameter, assume I want to give this myObj to the controller, prototypical inheritance, function and all:
var proto = {
p1: "p1",
pf: function() {
return "proto"
}
};
function MyObj(ost) {
this.ost = ost;
}
MyObj.prototype=proto;
var myObj = new MyObj("OST");
So I have myObj, and I have a string:
<div ng-app='myApp' ng-controller="MyCtrl">....</div>
I put the string in the DOM, and call angular.bootstrap().
How to I get the real myObj object into MyCtrl's $scope for this <div>, not a serialized/deserialized version/copy of it?
Services is what you are looking for.
You can create your own services and then specify them as dependencies to your components (controllers, directives, filters, services), so Angular's dependency injection will take care of the rest.
Points to keep in mind:
Services are application singletons. This means that there is only one instance of a given service per injector. Since Angular is lethally allergic to global state, it is possible to create multiple injectors, each with its own instance of a given service, but that is rarely needed, except in tests where this property is crucially important.
Services are instantiated lazily. This means that a service will be created only when it is needed for instantiation of a service or an application component that depends on it. In other words, Angular won't instantiate services unless they are requested directly or indirectly by the application.
Services (which are injectable through DI) are strongly preferred to global state (what isn't), because they are much more testable (e.g. easily mocked etc) and "safer" (e.g. against accidental conflicts).
Relevant links:
Understanding Angular Services
Managing Service Dependencies
Creating Angular Services
Injecting Services into Controllers
Testing Angular Services
About Angular Dependency Injection
Example:
Depending on your exact requirements, it might be better to create one service to hold all configuration data or create one service per widget. In the latter case, it would probably be a good idea to include all services in a module of their own and specify it as a dependency of your main module.
var services = angular.module('myApp.services', []);
services.factory('widget01Srv', function () {
var service = {};
service.config = {...};
/* Other widget01-specific data could go here,
* e.g. functionality (but not presentation-related stuff) */
service.doSomeSuperCoolStuff = function (someValue) {
/* Send `someValue` to the server, receive data, process data */
return somePrettyInterestingStuff;
}
...
return service;
}
services.factory('widget02Srv', function () {...}
...
var app = angular.module('myApp', ['myApp.services']);
app.directive('widget01', function ('widget01Srv') {
return function postLink(scope, elem, attrs) {
attrs.$set(someKey, widget01Srv.config.someKey);
elem.bind('click', function () {
widget01Srv.doSomeSuperCoolStuff(elem.val());
});
...
};
});
ExpertSystem's answer gave me the hint that I needed. A separate controller instance for each widget. Note how the constructorParameter (==myObj) gets inserted into the controller.
function creatWidgetInstance(name) {
....
var controllerName = name + 'Ctrl';
// myObj comes from the original question
var constructorParameter = myObj;
widgetApp.controller(controllerName, function($scope) {
$scope.string = constructorParameter;
});
....
newWidget = jQuery(...);
newWidget.attr('ng-controller', controllerName);
angular.bootstrap(newWidget[0], ['widgetApp']);
....
}
See it working in a plunker
Perhaps a more beautiful solution is with a separate service too, as in:
function creatWidgetInstance(name) {
....
var controllerName = name + 'Ctrl';
var serviceName = name + 'Service';
// myObj comes from the original question
var constructorParameter = myObj;
widgetApp.factory(serviceName, function () {
return { savedConstructorParameter: constructorParameter };
});
widgetApp.controller(controllerName,
[ '$scope', serviceName, function($scope, service) {
$scope.string = service.savedConstructorParameter;
}
]
);
....
newWidget = jQuery(...);
newWidget.attr('ng-controller', controllerName);
angular.bootstrap(newWidget[0], ['widgetApp']);
....
}
See this in a working Plunker
The answer to the question requires backtracking a few assumptions. I thought that the only way to setup $scopewas to do it on a controller. And so the question revolves around how to "provide data to an Angular controller other than through attributes in the DOM handled by directives".
That was misguided.
Instead, one can do:
var scope = $rootScope.$new();
// Actually setting scope.string=scope makes for a horrible example,
// but it follows the terminology from the rest of the post.
scope.string = myObj;
var element = $compile(jQuery('#widgetTemplate').html())(scope);
jQuery('#widgets').append(element);
See this Plunker for a working example.