Angular JS Factory Clarification - angularjs

what is the difference between all these Factory definitions
app.factory('myFirstFactory', function () {
var customShow = function () {
return "My First Factory";
}
return customShow;
});
app.factory('mySecondFactory', function () {
return {
show: function () {
return "My Second Factory";
}
}
});
app.factory('myThirdFactory', function () {
function myCustomShow() {
return "My Third Factory";
}
return {
show: myCustomShow
}
});
Here is how its been called in the controller. What is the ideal case of defining the factory. What is the actual return type from the factory, In one defintion, it could seems like Factory and Service are look alike. Can someone please clarify
$scope.message1 = myFirstFactory();
$scope.message2 = myService.show();
$scope.message3 = mySecondFactory.show();
$scope.message4 = myThirdFactory.show();

The first one returns customShow. customShowis declared as a function. So the service returned by this factory is a function. So, when you inject myFirstFactoryin a controller or another service, the injected value will be a function. BTW, you shouldn't choose xxxFactoryas the name. The component you're defining, thanks to a factory, is a service, not a factory. What is injected is the returned service, not its factory.
The second one and the thirst one both return an object:
return {
show: ...
}
So what will be injected in both cases is an object, and not a function like in the first case.
The returned object has a single field named show, which is a function. The second one defines a named function, and assigns this named function to the showproperty of the returned object, whereas the third one directly assigns an anonymous function to the showproperty of the returned object. But the end result is the same. The only difference you'll see is when printing (for debugging) the function itself:
console.log(theService.show);
will print
function myCustomShow() { ... }
for the second one, but will print
function () { ... }
for the third one.

It's all about your preference how to handle the interface of the factory. Another point is best practice. The third one is suggested by John Papa in his detailed angular-styleguide even though he puts the the interface at the top and not below all functions due to readability. It's actually derived by the Revealing Module Pattern.
The first remindes me of a class definiton if there is only a single function returned by the factory. Thus it has to be invoked when you inject it into your controller as follows:
function MyController($scope, myFirstFactory)
{
// $scope.myFirstFactory would print out "My First Factory"
$scope.myFirstFactory = myFirstFactory();
}
This is usually used if you plan to write object-oriented AngularJS services, since factories are useful to define a classes that you can instantiate many times using the new keyword, while services always create singletons.
app.factory('MyFirstFactory', function() {
var privateVariable = 'foo';
// Constructor
var MyFirstFactory = function(bar) {
this.bar = bar;
};
MyFirstFactory.prototype.showCustom = function() {
return "My Third Factory";
};
return MyFirstFactory;
});
You could then create many instances like so:
function MyController($scope, myFirstFactory)
{
// $scope.myFirstFactory would print out "My First Factory"
$scope.myFirstFactory = new MyFirstFactory();
$scope.showCustom = myFirstFactory.showCustom();
}
The second is a variation of the third one which both return an object as #jb-nizet mentioned.

Related

Jasmine How to test a function that returns an anonymous function

I have the following code in a service.
function getK(k) {
return function() {
return k;
};
}
I can test that getK was called with
beforeEach(inject(function(FiltersService){
this.service = FiltersService;
spyOn(this.service, 'getK');
}));
it("getK should be called", function(){
var key = "TEST";
var result = this.service.getK(key);
expect(this.service.getK).toHaveBeenCalled();
expect(this.service.getK).toHaveBeenCalledWith("TEST");
}
My question is how do I test the inner
return function() {
return k;
}
With Jasmine
EDIT
To try and further specify, the getK function is used to fill in an object as shown below. That object is part of a library that I am using that uses key. I guess I am a little confused on how to test this from a karma-jasmine perspective seeing that the actual implementation of key is part of a library that I do not control. So I am not sure how to write tests for the internal getK function or even for the below valid function to be sure the object is filled out properly
libObject.push({
Data: {
Name: 'test'
},
key: getK("TEST"),
valid: function() {
return true;
}
});
Unit testing supposes testing the code line by line for full coverage.
Considering that getK is exposed as FiltersService method, it is
expect(this.service.getK).toEqual(jasmine.any(Function));
var key = {};
var result = this.service.getK(key);
expect(result).toEqual(jasmine.any(Function));
expect(result()).toBe(key);
There's no need for a spy in this spec, because there's nothing to spy.
Since calling the parent function returns a function, call the parent function, and immediately call the anonymous function returned by the parent:
expect(service.getK(k)()).toEqual(k);
This is very simple and should work perfectly for you.

Angular - Moving function into a service

I have a function that I'm now needing to use in multiple page so decided to move into a service - however its not going as I'm expecting.
So in my HTML i have:
<li ng-init="bg = underQBar(work.options)">
Then in the controller (before I moved the common function) it looked like:
$scope.underQBar = function(toWorkArray) {
//some implementation, with a return at the end
}
Now I've made my service:
function barService($window){
var self = this;
self.getBarColours = function(toWorkArray) {
//copied the implementation with the return here
}
}
And therefore trying this in my controller:
$scope.underQBar = barService.getBarColours(toWorkArray);
However this doesnt work, its not getting the parameter I'm sending from the HTML - is this a trivial fix or something wrong with the implementation?
This is the problem:
$scope.underQBar = barService.getBarColours(toWorkArray);
Here, you're assigning the result of the service function call to $scope.underQBar, when you meant to assign it the function itself.
This should work:
$scope.underQBar = barService.getBarColours;
If you want to make it more clear to the reader that it's a function, just do:
$scope.underQBar = function (toWorkArray) {
return barService.getBarColours(toWorkArray);
}
Here is a correct definition for your service :
angular.module("myModule").factory("BarService", ["$window", function($window){
var service = {};
service.getBarColours = function(toWorkArray){
//copied the implementation with the return here
};
return service;
}]);
And here is a correct way to inject the service in your controller :
angular.module("myModule").controller("controllerName", ["BarService", function(BarService){
var self = this;
self.getBarColours = BarService.getBarColours;
}]);
and here is the way to use it:
<li ng-init="bg = controllerName.underQBar(work.options)">
Explanation :
A service or a factory in angular cannot be accessed by your view. Your view can only make a call to your controllers.
If your function should have been called by many controllers, you can put this function in a global controller that will be responsible for controlling your whole view.

Can AngularJS Factory return two different instances

'use strict';
(function(){
function myFactory(){
var model = {};
function viewModelManager(){
return {
getInstance : function(){
return model;
}
}
return viewModelManager;
}
}
angular.module('main').factory('viewModelFactory',myFactory);
} ());
In reference to above code snippet, I want to know the difference between below two invocations :
new viewModelFatory();
and
viewModelFactory();
Both seem to be returning object which is returned by viewModelManager function. Then what is the advantage of using operator new?
Also is my understanding correct about above code snippet :
1) that viewModelFatory will only have one single instance and whenever we inject it into multiple controllers it will refer to that instance.
2) that viewModelFactory can be used to create multiple stateful instances given that I create these instance variables inside viewModelManager function?
3) whenever I call new viewModelFatory()(OR viewModelFactory()), it will create a new instance of viewModelManager function?
Can AngularJS Factory return two different instances?
AngularJS Providers are singletons. Using the new keyword will not create a new factory. However methods of a factory can create new instances.
As example, the getInstance method of your factory can create new instances.
'use strict';
(function(){
function myFactory(){
var modelID = 0;
function Model(id) {
this.id = id;
};
function viewModelManager(){
return {
getInstance : function() {
return new Model(modelID++);
}
}
return viewModelManager;
}
}
angular.module('main').factory('viewModelFactory',myFactory);
} ());
In the above example, the first call to viewModelFactory.getInstance() will return an instance of the Model object with the id property equal to zero. The next call to viewModelFactory.getInstance() will return an instance with the id property incremented to one. Subsequent calls will create instances with the id property similarly incremented.
To reiterate, AngularJS factories are singletons. However factory methods can create different instances.
From MDN:
The new operator creates an instance of a user-defined object type or
of one of the built-in object types that has a constructor function.
With new, unlike new() you can call something like this:
new factoryService.ConstructorFactory();
So you could return different methods by calling them:
var ConstructorA= function () {
return 'A'
};
var ConstructorB= function () {
return 'B';
};
return {
ConstructorA: ConstructorA,
ConstructorB: ConstructorB
};
So in your code you are returning a viewModelManager instance
I am assuming the code is supposed to look like this:
'use strict';
(function(){
function myFactory(){
var model = {};
function viewModelManager(){
return {
getInstance : function(){
return model;
}
}
return viewModelManager;
}
}
angular.module('main').factory('viewModelFactory',myFactory);
} ());
note the placement of the return viewModelManager line.
That said, if my assumption is correct, then here is what happens:
The first time 'viewModelFactory' is found as an injected dependency in another component, angular will execute the myFactory function. The viewModelFactory is equal to the return value of the myFactory function which is viewModelManger. Then, if you use like this:
new viewModelFactory()
you will be given a new viewModelManger object (prototype viewModelManager) (i.e. the viewModelManager function is executed as a constructor), this object will not have any properties.
Whereas if you use is like this:
viewModelFactory()
you will be given the return value of the viewModelManager function which will have prototype Object and a method getInstance.
Edit:
Hmmm nevermind, I just learned that if you return explicitly from a constructor, that will override default constructor behavior.

How to create any kind of immutable providers in AngularJS

Consider the following example:
angular.module('demo')
.service('MyService', function () {
this.fn = function () {
console.log('MyService:fn');
};
})
.factory('MyFactory', function () {
function fn() {
console.log('MyFactory:fn');
}
return { fn: fn };
})
.value('MyValue', {
fn: function () {
console.log('MyValue:fn');
}
})
.constant('MyConstant', {
fn: function () {
console.log('MyConstant:fn');
}
})
.run(function (MyService, MyFactory, MyValue, MyConstant) {
MyService.fn();
MyFactory.fn();
MyValue.fn();
MyConstant.fn();
MyService.fn = undefined;
MyFactory.fn = undefined;
MyValue.fn = undefined;
MyConstant.fn = undefined;
})
.run(function (MyService, MyFactory, MyValue, MyConstant) {
MyService.fn();
MyFactory.fn();
MyValue.fn();
MyConstant.fn();
});
When the first run() is executed, all 4 console logs will be executed and print something on the console. Then I set each of the providers fn function to undefined for simplification purposes, say someone rewrote this function somewhere (which is something I want to prevent).
On the second run() block, everything is undefined and errors will be thrown. I'm confused by this... Shouldn't at least some of them (constant is the first to come to mind) be immutable objects?
Is this the expected behavior or am I doing something wrong?
Why is this a surprise. Angular is a framework running on top of Javascript, and Javascript is a dynamic language. Your question is really about the language construct.
First, recognize that all the calls are, at the end of the day, registering a provider that would return an object to be injected. .service, .factory, and .value are just short hands for .provider (.constant is a bit different).
Having established that there is no difference between them once the object is injected, all you then need to concern yourself with is how to make that object immutable.
Javascript provides Object.freeze function, so for example, you could do:
var immutable = {
fn: function () {
console.log('MyConstant:fn');
}
};
Object.freeze(immutable);
app.constant("MyConstant", immutable);
The functions constant, factory, service etc. allow you to create Javascript objects that are created once, then cached by angular, and injected into components as/when needed. Because these are Javascript objects, then (ignoring the possibility of using freeze) any bit of code has access to these objects can modify properties on them, as you have demonstrated.
Although properties on the objects themselves can be modified, the object itself can't be changed to another one, so if you really want an immutable provider, that is safe from all tampering, one way that I can think of is to use a factory that returns not an object, but a getter function:
app.factory('MyFactory', function() {
var functions = {
myFunctionName: function() {
}
};
return function(functionName) {
return functions[functionName];
};
});
which can be used, say in a controller, as
app.controller('MyController', function(MyFactory) {
MyFactory('myFunctionName')();
});
A drawback of using this over the more traditional way of exposing all the methods and allowing the possibility of the object to be modified is that I suspect unit testing could be more complicated: you wouldn't be able to easily create spies on your factory methods by using createSpy(MyFactory, 'myFunctionName').

angularjs initiating controller scope from a service

I want to be able to preserve scope data between route changes, so I created a service to return saved data to a controller for it to initiate on scope.
app.factory('answerService', function () {
var answers = {
1 : { text : "first answer" },
2 : { text : "second answer" }
}
return {
getAllAnswers: function () {
return answers;
},
getFirstAnswer: function () {
return answers[1];
}
}
});
In my controller, I initiate the first answer by calling the service to get the first answer.
app.controller('myController', function ($scope, answerService) {
$scope.firstAnswer = answerService.getFirstAnswer();
$scope.answers = answerService.getAllAnswers();
});
http://jsfiddle.net/hientc/gj5knrg7/2/
The problem I'm having is that $scope.firstAnswer is somehow also being binded to $scope.answers. I don't want that, I only want the input to bind to scope.firstAnswer.text
What am I doing wrong?
This is because answers[1] is an object reference, and assigning its value to another variable signifies that the variable is a reference to that object. In order to get a copy of that value you can copy it using angular.copy().
Simply change getFirstAnswer() function to something like this:
DEMO
getFirstAnswer: function () {
return angular.copy(answers[1]);
}

Resources