How to create any kind of immutable providers in AngularJS - 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').

Related

How do I define constructor in service and factory in Angular?

i am little bit familiar with angular. still on learning process. working with ng version 1.4.8. so i like to know how could i define constructor function in service and factory.
here is one sample service .now tell me how to define constructor in service or factory ?
angular.module('myApp').service('helloService',function($timeout){
this.sayHello=function(name){
$timeout(function(){
alert('Hello '+name);
},2000);
}
});
angular.module('myApp').controller('TestController',
function(helloService){
helloService.sayHello('AngularJS'); // Alerts Hello AngularJS
});
The function you pass to .service gets called with new, thus it is already basically a constructor. It is a "constructor function" and it implicitly returns an object, which is the singleton:
angular.module('myApp').service('helloService',function($timeout){
// This constructor function implicitly returns an object. It is called
// only once by Angular to create a singleton.
this.sayHello = function(name) {
// etc
}
});
Just to illustrate, if you passed an ES6 class to .service (which does have a constructor) instead of a constructor function, that constructor would be called when the singleton is created:
class HelloService {
constructor($timeout) {
// Called once when the singleton is created
}
sayHello(name) {
// etc
}
}
angular.module('myApp').service('helloService', HelloService);
Using .factory is similar, but it doesn't get called with new. So the function you use in this case has to return a singleton object explicitly:
angular.module('myApp').factory('helloService',function($timeout){
// This factory function explicitly returns an object. It is called
// only once by Angular to create a singleton.
return {
sayHello: function(name) {
// etc
}
};
});
Edit: As mentioned by #Vladimir Zdenek, these "constructors" cannot be used to configure the singleton externally. However, I interpreted the question to mean "Where can I put code that runs when the singleton is created?". Singletons may need to initialize data, so that initialization can go in the "constructor".
There is (probably in most cases) no need for a constructor when it comes to singletons. To require such a thing might be just pointing to a bad architectural design of your application.
That said, you can make a global configuration available for your service/factory by using a provider. You can find more on providers in the Official Documentation.
If you do not need a singleton and you wish to create a reusable piece of code, you can use something (in JavaScript) known as factory functions. You can see an example of such function below.
function MyFactory(myParams) {
const Factory = {
// Properties
myProperty: myParams.myProperty,
// Methods
getMyProperty: getMyProperty
};
return Factory;
function getMyProperty() {
return Factory.myProperty;
}
}
// usage
const myObj = MyFactory({ myProperty: 'Hello' });

How to apply watcher on a variable without using $scope?

Is there any way to watch changed value of a variable without using $scope?. My manager told me that we may migrate our code to angular2 which do not have $scope so we have to write the code in the way so that when we migrate it to angular2 it needs minimal changes. So now i want to keep track changes of a variable which we do in angular like :
$scope.$watch('myVar', function() {
alert('hey, myVar has changed!');
});
Now i want this to something like
app.controller('auditCtrl',
function($scope, $localStorage) {
var adc = this;
angular.extend(this, {
$state: $state,
count: 0
}
//What i want is something like
abc.$watch('count', function() {
alert('hey, count has changed!');
});
})
No, thats not possible. But ask yourself, what is the variable you want to $watch for? If it is a form field, you can simply use ng-change.
If you can use es2015 Syntax, you can add Getters and Setters on the Controllers Prototype which then triggers an update Function.
Something like this:
let auditCtrl = function () {
const adc = self;
adc.counterItem = null;
// etc.
};
Object.defineProperty(auditCtrl.prototype,
"counterItem", {
get: function () {
return this.counterItem;
},
set: function (newValue) {
this.counterItem = newValue;
// Call method on update
this.onCounterItemChange(this.counterItem);
},
enumerable: true,
configurable: true
});
Or, in my opinion the best approach, use a component architecture for your application. With components, you can use some built-in lifecycle-hooks like $onInit, $onChanges etc. This way, you are also thinking in the angular2 way, since you might want to migrate.

Why anglar.mock.module does not $provide values like it $provides constants

I've looked at the documentation for angular.mock.module and a couple of examples of others using it but I seem to be running into an issue in my use-case that I don't understand.
I'm running Jasmine (2.4.1) tests with angular (1.4.9) and I have my angular app separated into multiple modules. When I attempt to mock out certain parts of my app for unit testing I want to mock out entire modules (or providers) so that I only expose the pieces I use.
Here is a very simple app that has a main module plunker which depends on plunker.service. plunker.service depends on plunker.constant.
var app = angular.module('plunker', ['plunker.service']);
app.controller('MainCtrl', function($scope, valueService, appService) {
$scope.init = function() {
$scope.appValue = valueService.getValue();
$scope.appIsRunning = appService.getStatus();
};
});
angular.module('plunker.service', ['plunker.constant'])
.service('appService', function(appSettings) {
var vm = this;
vm.getStatus = function () {
if (appSettings.isRunning) {
return true;
} else {
return false;
}
};
})
.service('valueService', function(valueSettings) {
var vm = this;
vm.getValue = function () {
return valueSettings.value;
}
});
angular.module('plunker.constant', [])
.constant('appSettings', { isRunning: true })
.constant('valueSettings', { value: 10 });
In my Jasmine tests I have a beforeEach() that registers my modules using module (aka angular.mock.module).
I have seen 3 ways of using module
string
function with $provide
object
You can see below that I use the module('plunker') (string) to register my main module and I have 3 ways of mocking out my appSettings constant (A, B, C). You will notice that the function with $provide.constant works fine but function with $provide.value does not and object does not.
beforeEach(function() {
module('plunker');
function useFunction(typeofProvider) {
module(function($provide) {
$provide[typeofProvider]('appSettings', { isRunning: false });
});
}
function useObject() {
module({
appSettings: { isRunning: false }
});
}
// A. THIS WORKS! //
useFunction('constant');
// B. THIS DOES NOT //
// useFunction('value');
// C. THIS ALSO DOES NOT!! //
// useObject();
inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
ctrl = $controller('MainCtrl', {
$scope: $scope
});
});
});
I have also seen people use the following syntax...
beforeEach(function() {
var mockService = function () {
var mockValue = 10;
this.value = mockValue;
};
// D.
module('a.module.name', function newProviders($provide){
$provide.service('realService', mockService);
});
});
My questions
In my test code, why does A. work but B. and C. do not?
Is D. equivalent to calling module('a.module.name'); followed by module(function newProviders($provide) { ... });? Does placing both in the same module() call have any special effects on how things are registered or is it just a shorthand? (based on the documentation it should be a shorthand)
Related to Jasmine, specifically, do all beforeEach() calls run in the same top-to-bottom order with every execution?
Here is my plunker for the above app and jasmine code
Thanks
This happens because of how Angular injector works. In fact, there are two different injectors in Angular. The one (available as $injector in config blocks) deals with service providers. Another one (available as $injector anywhere else) deals with service instances. Providers and instances are cached and stored internally.
$provide.constant('service') creates both provider and instance of name 'service' at call time.
All other types of services are lazily instantiated. They create 'serviceProvider' provider at call time, but 'service' instance is created on the first injection.
Since Angular service instance is a singleton, it refers to instance cache before the instantiation. If the instance is in the cache, it is reused and not instantiated. constant service instance is eagerly instantiated, so only another constant can override the instance.
Object properties in angular.mock.module are shortcuts for $provide.value, and useObject() equals to useFunction('value') in this example.
As long as module order stays the same,
module('a.module.name', function ($provide) { ... });
is indeed a shortcut for
module('a.module.name');
module(function ($provide) { ... });
Due to the fact that appSettings object isn't used in config blocks (the primary use of constant service), it is more convenient to make it value.

dependency injection into decorator functions in angularJS

When using angularJS you can register a decorating function for a service by using the $provide.decorator('thatService',decoratorFn).
Upon creating the service instance the $injector will pass it (the service instance) to the registered decorating function and will use the function's result as the decorated service.
Now suppose that thatService uses thatOtherService which it has injected into it.
How I can I get a reference to thatOtherService so that I will be able to use it in .myNewMethodForThatService() that my decoratorFN wants to add to thatService?
It depends on the exact usecase - more info is needed for a definitive answer.
(Unless I've misunderstood the requirements) here are two alternatives:
1) Expose ThatOtherService from ThatService:
.service('ThatService', function ThatServiceService($log, ThatOtherService) {
this._somethingElseDoer = ThatOtherService;
this.doSomething = function doSomething() {
$log.log('[SERVICE-1]: Doing something first...');
ThatOtherService.doSomethingElse();
};
})
.config(function configProvide($provide) {
$provide.decorator('ThatService', function decorateThatService($delegate, $log) {
// Let's add a new method to `ThatService`
$delegate.doSomethingNew = function doSomethingNew() {
$log.log('[SERVICE-1]: Let\'s try something new...');
// We still need to do something else afterwards, so let's use
// `ThatService`'s dependency (which is exposed as `_somethingElseDoer`)
$delegate._somethingElseDoer.doSomethingElse();
};
return $delegate;
});
});
2) Inject ThatOtherService in the decorator function:
.service('ThatService', function ThatServiceService($log, ThatOtherService) {
this.doSomething = function doSomething() {
$log.log('[SERVICE-1]: Doing something first...');
ThatOtherService.doSomethingElse();
};
})
.config(function configProvide($provide) {
$provide.decorator('ThatService', function decorateThatService($delegate, $log, ThatOtherService) {
// Let's add a new method to `ThatService`
$delegate.doSomethingNew = function doSomethingNew() {
$log.log('[SERVICE-2]: Let\'s try something new...');
// We still need to do something else afterwatds, so let's use
// the injected `ThatOtherService`
ThatOtherService.doSomethingElse();
};
return $delegate;
});
});
You can see both approaches in action in this demo.

Angular JS Factory Clarification

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.

Resources