How to make an Angular factory globally active? - angularjs

I am using Angular Websockets to define a factory which is intended to be globally active. The current factory is defined as:
ngApp.factory('FixtureFeed', [
'$websocket', '$rootScope', function($websocket, $rootScope) {
var dataStream;
dataStream = $websocket("ws://" + window.location.hostname + "/");
dataStream.onMessage(function(message) {
return $rootScope.$broadcast('message', message.data);
});
return dataStream;
}
]);
And then I simply include it as a dependency on one of my controllers, such as:
ngApp.controller('FixtureCustomCtrl', function($scope, $http, $rootScope, FixtureFeed) {
# other code...
});
And simply having it there and not touching it further appears to allow it to be active beyond the life of the controller, where by if I switch to a different angular path which calls a different controller, the FixtureFeed continues to handle messages and broadcast.
I could include the factory as a dependency on all my controllers, and this appears to work fine, only ever instantiating the factory once and handling each message once, but it feels like a dirty hack of course. I also can't guarantee that any given controller will become immediately active as I have all sorts of routes that could be the entry point to using the app. What is the correct way to handle this please?

Related

Is there a way to initialize an Angular Service without DI into controller

I have a service like this
function BroadcastClock($interval, $rootScope){
this.ticker = $interval(function(){
console.log("Tick");
$rootScope.$emit('clockTick');
}, 1000);
}
function ClockService($interval, $rootScope) {
var clock = new BroadcastClock($interval, $rootScope);
}
This does not work unless I change the controller to declare
app.controller('MainCtrl', function($scope, $rootScope, clockService)
In more complex situations (say multiple routes) this adds an extra injection for really no reason. What is the better way to handle this?
Trying to clarify a bit (I don't get the -1 but whatever)
The problem I have here is that I do not need to initialize the service using an actual controller. This service in fact feeds into multiple controllers. The singleton aspect helps me keep a single clock for all controllers.
However, the controllers themselves do not interact with it only via the proxy of the rootScope broadcast so I don't see the need to inject it into the actual controller.
See the plunker for more info it won't work, however, when you change
app.controller('MainCtrl', function($scope, $rootScope)
//To
app.controller('MainCtrl', function($scope, $rootScope, clockService)
it works fine, we are not "doing" anything with the service except listening for broadcasts so it doesn't make any sense to inject.
Because you instantiate BroadcastClock, I think you still should be able to handle it via a service by either exposing it or exposing functions that handles it.
Anyway you can run initializing blocks with .config(), at provider injection time, or in your case .run() at the beginning at application's run time:
app.run(function($interval, $rootScope) {
var clock = new BroadcastClock($interval, $rootScope);
});

REST call on application init phase and defining constants based on REST call

I was wondering what the best moment is while initializing the app to retrieve data from:
1 - a REST service
2 - $routeParams
to define application wide constant.
config phase only accepts providers and during the config / run phase $routeParams properties are undefined.
This seems like somewhat in the right direction:
Can you pass parameters to an AngularJS controller on creation?
Also: how to define a constant within a controller, is that possible?
app.controller('MainCtrl', function($scope) {
//define app.constant here
}
--edit: typo
During run phase all the providers should be initialized and working correctly, you can use the $http service to retrieve what ever parameters are needed for you.
I am pretty sure the $routeParams are initialized at run phase as well.
Defining constants in a controller isn't a good practice (in my opinion), if they are unique to that controller then they are just variables, and if you want real application wide constants, use a service, that's what they are for :)
I know of one easy way to pass parameters to controllers on creation, which is using the angular ui router project: https://github.com/angular-ui/ui-router
In the resolve function you can do http calls if necessary, inject constants etc, it's very handy and personally I never build an angular project without it.
I am pretty sure there are more ways, but usually the best practice to pass data between controllers is using a service.
On a side note, if you have a piece of data that is common to more than 1 controller, the easiest way is to put that data on a service and do a watch on that service return value, for example, say I have isLoggedIn, which can change at any moment and a lot of controllers will want to be notified about it, use a UserService and watch for it's value:
UserService.isLoggedIn = function() {
return _isLoggedIn;
}
And in your Controller:
$scope.$watch(function() {
return UserService.isLoggedIn();
}, doSomeAction);
I hope this helps!
This http://www.jvandemo.com/how-to-resolve-angularjs-resources-with-ui-router/ seems like a nice guide, basically, you add the resolve to the state:
.state("customers", {
url : "/customers",
templateUrl: 'customers.html',
resolve: {
//any value you want, this function should return a promise,
//only when that promise is resolved, it will instantiate the controller
//Make sure however you add some signal that something is happening because
//while fetching it can seem like the page is not responding
customers: ['$http', 'anyOtherServiceYouMightNeed', function($http, otherService){
//return a promise
return $http.get('api/customers');
}],
//and for constant
constants: ['ConfigService', function(config) {
return config.appConstants;
}]
},
//customersCtrl will have customers resolved already
controller : 'customersCtrl'
});
app.controller('customersCtrl', ['$scope', 'customers', 'constants',
function($scope, customers, consts) {
//customers will be ready and resolved when the controller is instantiated
//you can do this with anything you might need inside a controller
}

AngularJS: unknown provider until after page loads?

So this is really weird, maybe it has a simple answer I'm missing. The following code gives an unknown provider error:
var foo = angular.module('foo', [ 'ngRoute', 'ngAnimate', 'ngCookies' ]);
foo.factory('fooApi', function ($scope, $http) {
var url = '/api/';
var factory = {};
factory.action = function (fields) {
fields.userid = $scope.userid;
fields.token = $scope.token;
console.log(JSON.stringify(fields));
return $http.post(url, { data: fields });
};
return factory;
})
.controller('loginController', function ($scope, fooApi) {
// do stuff
});
It's all loading together in the same file, and I'd think the factory being first would resolve and the injector would be able to find it when referenced below. But it gives an unknown provider error.
However, if I comment out the controller and wait for the page to load and then do the exact same controller declaration in the Chrome JS console it works fine.
Anyone run into this before and know how to deal with it? I haven't been able to find this same exact issue anywhere.
Like #tasseKATT said, you can not inject $scope into a service, particularly a factory. Maybe your confusion is because $scope can be injected in controllers, so you tried to injected into a factory.
An interesting thing is that the $scope that you see being injected into controllers is not a service - like the rest of the injectable stuff -, but is a Scope object.
The main purpose of $scope is a king of glue between views and controllers, it doesn't make much sense to pass a $scope into a service.
The services only have access to the $rootScope service.
If you need to pass the $scope of a specific controller to a service always you can pass it like parameter of a function in the service. This approach is not recommended because starting to break the SoC and the single responsibility principle, but maybe could fit you.
Good luck :-)

How can I register an angularjs factory after the app has "started" / been loaded?

I've got the guts of a routing architecture in Angular that will dynamically download and inject Angular Controllers and Services ... the Controller part works fine, and I'm trying to download dependant services via $route's .resolve property.
Now, say if I have a factory declared in scope while the page starts up, it registers fine and Controllers that use it resolve fine, e.g:
myModule.factory('MyInjectedDep', function() {
return {};
});
....
MyController = function(MyInjectedDep)
But if I try and register that dependency at "run time" (for want of a better phrase), I get a Circular Dependency error. e.g:
$route.routes[routeItem.route] = {
resolve: {
MyInjectedDep: ['$injector', function($injector) {
// In real code I download/eval this via $http but same behavior occurs
myModule.factory('MyInjectedDep', function() {
return {};
});
}]
}
}
So when my Controller is then initiated:
MyController = function(MyInjectedDep)
I get a circular dependency error, but no dependency trace in the error message?
Error: Circular dependency:
Any ideas appreciated
The key is latching onto $provide at configuration time. If you grab $provide at configuration time and maintain a reference to it, you can use it to register your factory like:
$provide.factory.apply(null, ['MyInjectedDep', [function() {
return {};
]}]);
I have a provider/service designed to do this, adapted from some other samples on github: https://github.com/afterglowtech/angular-couchPotato .
It's primarily designed to load from AMD, but you could probably use it's registerXXX functions with $http, or at least copy the relevant portions of its code. Don't let the size of the repository fool you -- the actual provider/service is about one page of code https://github.com/afterglowtech/angular-couchPotato/blob/master/src/couchPotato.js .

Can .$inject be used on Services in AngularJS, or is it needed only for Controllers?

I'm aware that for the purposes of minification and obfuscation we should always use the $injector (by way of controllerName.$inject = ['$service', '$service2']) to specify the actual service names that are required.
If I write a custom service that relies on other services, however, can/should I do the same thing? The only examples I can find for using the .$inject method are called on Controllers.
If I am doing
myModule.factory('myService', function($rootScope, anotherService) {
return {
foo: 'bar'
});
Should I append this?
myService.$inject = ['$rootScope', 'anotherService'];
Or perhaps it's applied to the module as a whole then?
myModule.$inject = ['$rootScope', 'anotherService'];
...But maybe in that case, the module is already keeping track of its services, and hence minification is not a concern?
Check the dependency injection guide, section Inline Annotation.
The following is also a valid syntax, and it is safe for minification:
myModule.factory('myService', ['$rootScope', 'anotherService',
function($rootScope, anotherService) {
....
}]);

Resources