private angular service or controller - angularjs

I have an angular module that contains some 'private' services. Those private services are only needed by other services in the same module and it makes sense to expose them for testing. But I don't want other modules to use this services.
Is there any possibility to mark such services 'private'? Is there at least a convention to name this services so that others recognize them as private?

If the only reason you need these "classes" to be angular Services is to have their dependencies injected painlessly, you could use $injector.instantiate to instantiate them without registering them as services.
Sample Code:
var PrivateClass = (function () {
function PrivateClass($log) {
this.hello = function () { $log.debug("Hello World!");}
}
PrivateClass.$inject = ["$log"];
return PrivateClass;
})();
angular.module('TestApp', []).run(['$injector', function ($injector) {
var p = $injector.instantiate(PrivateClass);
p.hello();
}]);
You would use $injector.instantiate(PrivateClass) in the constructor (or anywhere) within the services that need it, to create an instance of the PrivateClass. If you need PrivateClass to behave like a singleton (like a real angular service), you could use an accessor class that instantiate it once and returns its reference to callers.
The advantage of this is that you need not pollute the instantiating service's (in this case the function passed to angular.run), dependency array with dependencies that are only needed so they could be passed on to the PrivateClass ($log in this case).
I noticed that this is an year old question but, I found this while looking for a way to achieve exactly the above and ultimately solved it using this approach.

Related

Angular js - Hide private variables from factories

Pls refer - http://jsfiddle.net/36qp9ekL/629/
I am using a factory method so that i can hide the implementation logic, private(pvt) variables and all the pros that factories are meant for.
If you run the code and see the console, I am able to view pvtvar1 and pvtvar2. How do I actually hide the pvt variables, implementation details.
app.controller("mycontroller", function($scope, myfactory) {
console.log(myfactory);
});
If you could tell the advantages of factories over services, would be helpful.
app.factory("myfactory", function() {
var pvtvar1= 3;
var pvtvar2 = 4;
return {
a:function(){
return pvtvar2 + pvtvar1;}
}
});
You are declaring the object and the function on the same scope of the variables. That's why they are shown. Here I declare the function on the object's scope. Take a look at this for more details http://toddmotto.com/mastering-the-module-pattern/
As you know, there are no privates in javascript. You are using closure to create inaccessible variables and functions. This is as good as you'l get. The console may show you (depending on the browser) properties that are defined via closure, but the important thing is, these properties aren't accessible in your code.
Advantages of a service over a factory? You can create an injectable service from a js object instead of a function so you can create services that inherit functionality. Not terribly useful since the injectable model is a replacement for inheritance in many ways but it's still an option.
I exclusively write services instead of factories but I'm using typescript in my projects (which has privates ;))

Why shouldn't I access the factory function through a provider.$get in the config phase?

First of all, this is an honest question. And I'm looking for honest and justified answers on why I shouldn't be doing this...
angular
.module('X', ['Y'])
.config(function (myFactoryProvider, myServiceProvider) {
myFactoryProvider.$get().myFn();
myServiceProvider.$get().myFn();
});
angular
.module('Y', [])
.factory('myFactory', ['$location', function ($location) {
return {
myFn: function () {
console.log('factory');
console.log($location.absUrl());
}
}
}])
.service('myService', ['$location', function ($location) {
this.myFn = function () {
console.log('service');
console.log($location.absUrl());
}
}]);
Here's a JSFiddle: http://jsfiddle.net/1vetnu6o/
This is working as you can see above and it solves a few problems for me. But I shouldn't probably be doing this and I want to understand why. I really need good reasons to not do this. Despite the fact that I really want to.
tl;dr;
Here's the context of the problem...
I have this internal framework used by multiple products where there's this one service (which happens to be a factory) that basically contains a group of related helper methods. In this case device related like isMobileDevice, isAndroid, getDeviceType (with returns mobile, tablet or desktop), and few others...
This service must be injected into the config() phase of the application using the framework because we need access to the getDeviceType function. The thing is, we need to get the deviceType to load proper templates with $routeProvider. It's in the config() phase that we are building the correct template paths to be used for all the routes. Some of them depend on the deviceType, while others have a generic template independent of the device.
Since this is a service, we cannot inject it directly into the config() phase but we can call that method using the technique I mentioned earlier in the post.
How I'm currently solving this? The helper service is actually a provider and all the methods are exposed both in the provider section as well as in the factory function. Not ideal, but it works. I consider this a work-around, but I'd rather have a work-around in the application and not the framework, thus the technique first mentioned.
Thoughts?
I didn't knew but actually you can invoke on config phase.
The problem is that myService will be instantiated twice :/
angular
.module('X', [])
.config(function(myServiceProvider) {
myServiceProvider.$get().myFn();
})
.run(function(myService) {
myService.myFn();
})
.service('myService', ['$location', function($location) {
console.log('myService!');
this.myFn = function() {
console.log($location.absUrl());
}
}]);
output:
"myService!"
"location"
"myService!"
"location"
This is dangerous if you call $get on config of a service that has a big nested dependency tree :/
I think the correct approach, if you need a utility service (with no dependency), is to use a constant (in this way you can inject it everywhere). Otherwise if you need dependencies use a service and stick to the run() block.
The config() block it's the place to instruct your services how they should work with the help of their providers.
The run() block it's the perfect place to do some logic when your app starts (aka main method).

controllers are created using a factory function?

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.

Using $injector vs injecting a service directly

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.

How to provide initial data to Angular's controller/$scope?

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.

Resources