how to emit events from a factory - angularjs

How can I emit events from a factory or service. I am unable to inject $scope into the factory, thus unable to emit events.
I get the following error - Unknown provider: $scopeProvider <- $scope
Thanks,
Murtaza

Inject $rootScope instead of $scope and then emit it on the $rootScope.
myApp.factory('myFactory', ['$rootScope', function ($rootScope) {
$rootScope.$emit("myEvent", myEventParams);
}]);
Factories don't have access to the current controller/directive scope because there isn't one. They do have access to the root of the application though and that's why $rootScope is available.

You cannot inject a controller's scope into a service. What you can do is:
pass the scope instance as a parameter to one of your service functions:
e.g.
app.factory('MyService', function() {
return {
myFunction: function(scope) {
scope.$emit(...);
...
}
};
});
inject the $rootScope into your service:
e.g.
app.factory('MyService', ['$rootScope', function($rootScope) {
return {
myFunction: function() {
$rootScope.$emit(...);
...
}
};
}]);

In your factory inject $rootScope as-
myApp.factory('myFactory',function($rootScope){
return({
// use $rootScope as below to pass myEventParams to all below in hierarchy
$rootScope.$broadcast("myEvent",myEventParams);
})
}]);

Related

AngularJS: inject dependency into every controller

Suppose that I have a huge web application (that uses AngularJS) with a lot of controllers. Is there a way to inject $log service in every controller? To be more clear, I want to write something like this:
.config(function($log) {
allMyControllers.inject($log);
})
instead of
.controller('Controller1', function($log) {...})
.controller('Controller2', function($log) {...})
.controller('Controller3', function($log) {...})
.controller('Controller4', function($log) {...})
Possible thing that you can do is, create a controller that has all needed dependencies and make it as base controller and other controllers can extend it using angular extend api.
some clear example code which I came accross :
.controller('baseController', function(someService) {
this.someService = someService;
})
.controller('extendedController', function($scope, $controller) {
angular.extend(this, $controller('baseController', { $scope: $scope }));
this.alert = this.someService.alert;
})
.service('someService', function() {
this.alert = function() {
window.alert('alert some service');
};
});
Working solution of above code can be found here.

AngularJS 1.6 - On a config phase $http defaults requestError, broadcast event to main scope?

I need to broadcast an event after receiving a $http.defaults request error (400 status code, mainly) but I cannot access $rootScope within config phase.
Code:
var app = angular.module('app', []);
app.config(['$httpProvider','$injector', function($httpProvider, $injector) {
$httpProvider.interceptors.push(['$q', function($q) {
return {
'responseError': function (rejection) {
/*
Doesn't work:
$rootScope = $injector.get("$rootScope");
$rootScope.$emit("RESPONSE_ERROR");
*/
return $q.reject(rejection);
}
};
}]);
}]);
app.controller("Controller",function($scope){
$scope.$on("RESPONSE_ERROR",function(event,params){
alert("WORKING!");
});
});
Any suggestions? Thanks.
The problem is caused by the fact that there are two different injectors, as explained in this answer. In the code above $injector is the one that was injected during config phase, it deals with service providers.
Another one should be injected during run phase in order to get access to service instances like $rootScope:
$httpProvider.interceptors.push(function($q, $injector) { ... });
As another answer already explains, $rootScope can be injected into the interceptor directly. While $injector is a common way to avoid circular dependencies in interceptors and inject $http there.
You can try the following code injecting $rootScope into custom interceptor function.
var app = angular.module('app', []);
app.config(['$httpProvider','$injector', function($httpProvider, $injector) {
$httpProvider.interceptors.push(['$rootScope', '$q', function($rootScope, $q) {
return {
'responseError': function (rejection) {
$rootScope.$broadcast('RESPONSE_ERROR', {
// Custom properties
});
return $q.reject(rejection);
}
};
}]);
}]);

jasmine mocking service inside service

I'm testing a directive ('planListing') that has a dependency on a service called 'planListingService'. This service has a dependency to another service called 'ajax' (don't shoot the messenger for the bad names).
I'm able to compile the directive, load its scope and get the controller WITH A CAVEAT. As of now I am being forced to mock both services 'planListingService' and 'ajax' otherwise I will get an error like this:
Error: [$injector:unpr] Unknown provider: ajaxProvider <- ajax <- planListingService
http://errors.angularjs.org/1.3.20/$injector/unpr?p0=ajaxProvider%20%3C-%20ajax%20%3C-%20planListingService
I thought that because I was mocking up the 'planListingService' that I wouldn't have to actually bother with any implementation nor any dependencies of this service. Am I expecting too much?
Here is the code in a nutshell:
planListing.js
angular.module('myApp')
.directive('planListing', planListing)
.controller('planListingCtrl', PlanListingCtrl);
function planListing() {
var varDirective = {
restrict: 'E',
controller: PlanListingCtrl,
controllerAs: 'vm',
templateUrl: "scripts/directives/planListing/planListing.html";
}
};
return varDirective;
}
PlanListingCtrl.$inject = ['planListingService'];
function PlanListingCtrl(planListingService) {
...
}
planListingService.js
angular.module('myApp')
.factory('planListingService', planListingService);
planListingService.$inject = ['$q', 'ajax'];
function planListingService($q, ajax) {
...
}
ajax.js
angular.module('myApp')
.factory('ajax', ['backend', '$browser', 'settings', '$http', '$log',
function (backend, $browser, settings, $http, $log) {
...
planListing.spec.js
describe('testing planListing.js',function(){
var el,ctrl,scope,vm;
var service;
module('myApp');
module('my.templates');
beforeEach(module(function ($provide){
// This seems to have no effect at all, why?
$provide.service('planListingService', function () {
this.getAllPricePlans=function(){};
});
// I don't get the error if I uncomment this:
// $provide.service('ajax', function ($q) {
// this.getAllPricePlans=function(){};
// });
}));
beforeEach(function() {
module('myApp');
module('my.templates');
});
beforeEach(angular.mock.inject(function (_$compile_,_$rootScope_,_$controller_){
$compile=_$compile_;
$rootScope = _$rootScope_;
$controller = _$controller_;
el = angular.element('<plan-listing></plan-listing>');
scope = $rootScope.$new();
$compile(el)(scope);
scope.$digest();
ctrl = el.controller('planListing');
scope = el.isolateScope() || el.scope();
vm = scope.vm;
}));
describe('testing compilation / linking', function (){
it('should have found directive and compiled template', function () {
expect(el).toBeDefined();
expect(el.html()).not.toEqual('');
expect(el.html()).toContain("plan-listing-section");
});
});
it('should have a defined controller',function(){
expect(ctrl).toBeDefined();
});
it('should have a defined scope',function(){
expect(ctrl).toBeDefined();
});
});
So why is that I need to mock up the 'ajax' service even though I am mocking up 'planListingService' which is the one calling the 'ajax' service?
Thanks!
I have been there... feels like bad start But i think your directive is depend on the service and you need to inject it in order to directive can work with this, Just by calling directive it doesn't mean that it's going to inject it in your test. It will look for it and if it's not injected it will give you error
you could do so before testing your directive
beforeEach(inject(function ($injector) {
yourService = $injector.get('yourService');
})
For documentation purposes, here is the answer (thanks #estus for noticing this):
Indeed the problem was related to the incorrect initialization of my modules. Instead of this:
describe('testing planListing.js',function(){
var el,ctrl,scope,vm;
var service;
module('myApp');
module('my.templates');
...
I should've done this:
describe('testing planListing.js',function(){
var el,ctrl,scope,vm;
var service;
beforeEach(module('myApp'));
beforeEach(module('my.templates'));
...
After that things started working again as expected.

Can someone provide a use case for the $controller service in AngularJS?

Angularjs docs give the usage of $controller service as:
$controller(constructor, locals);
Can anyone focus some light on these 2 points:
When to use $controller service. Please provide some use case.
Details about 'locals' parameter passed to it.
You can create common functions which are to be executed on $scope into one controller may be named 'CommonCtrl'.
angular.module('app',[]).controller('CommonCtrl', ['$scope', function($scope){
var self = this;
$scope.stuff1 = function(){
}
$scope.stuff2 = function(){
}
self.doCommonStuff = function(){
// common stuff here
$scope.stuff1();
$scope.stuff2();
};
return self;
}]);
And inject this controller in other controllers let say 'TestCtrl1' like
angular.module('app',[]).controller('TestCtrl1', ['$scope','$controller', function($scope, $controller){
var commonCtrl = $controller('CommonCtrl',{$scope: $scope}); // passing current scope to commmon controller
commonCtrl.doCommonStuff();
}]);
Here, the in second argument of $controller service, we are passing dependencies that are required by CommonCtrl. So the doCommonStuff method will use TestCtrl1 controller's scope.
To mention one, it is useful in creating the target controller during unit testing.
Lets say you have a controller with signature .controller('MainCtrl', function($scope, serviceA){..}).
In testing,
// ...
beforeEach(inject(function ($rootScope, $controller, serviceA) {
// assign injected values to test module variables
scope = $rootScope.$new();
service = serviceA
// create the controller, by passing test module variables values as dependencies
$controller('MainCtrl', {'$scope': scope, 'serviceA': service});
}));
it('test on controller', function() {
//...
});
For more info checkout: https://docs.angularjs.org/guide/unit-testing
You can also use this service to achieve controller inheritance.
angular.module('app',[]).controller('BaseCtrl', ['$scope', function($scope){
$scope.action1 = function(){
console.log('In BaseCtrl action1');
}
$scope.action2 = function(){
console.log('In BaseCtrl action2');
}
}]);
angular.module('app').controller('ChildCtrl', ['$scope', function($scope){
angular.extend(this, $controller('BaseCtrl', {
$scope: $scope
}));
$scope.action1 = function(){
console.log('Overridden in ChildCtrl action1');
}
$scope.action2 = function(){
console.log('Overridden in ChildCtrl action2');
}
}]);

Unknown provider: {0} angularjs call a service

i get the error "Unknown provider: {0}", if i try to call a service of the submodule inside the submodule
here the mainModule script
var mainApp = angular.module("mainApp", ["categoriesApp","ui.router"]);
//a service in the mainmodule which i can call with no problems in the submodule
mainApp.factory("getDataSvc", ["$http", "$q", "path", function ($http, $q, path) {
return{
...
some $http
....
}]);
and now the submodule
var categoriesApp = angular.module("categoriesApp", []);
//i can inject and use getDataSvc with no problems from the mainmodule
categoriesApp.controller("listCtrl", ["$scope", "getDataSvc", function ($scope, getDataSvc){
getDataSvc.categories().then(function(data){
...
})
}])
//my service in the submodule
categoriesApp.factory("sharedDataSvc", ["$scope", function ($scope){
return{
getValue: function(){
return "oioioioi";
}
}
}])
//in this line i get the error, if i try to inject the sharedDataSvc
//if i dont inject it, i get no errors, but cant use the service ;)
categoriesApp.controller("addBtnCrtl", ["$scope", "sharedDataSvc", function ($scope, sharedDataSvc){
console.log(sharedDataSvc.getValue());
}])
hope somebody can tell me what i`m doing wrong ;)
The problem is with sharedDataSvc factory
You cannot inject $scope to a factory because $scope is not a registered provider.
$scope is only injected to controllers ( within a locals object ).
categoriesApp.factory("sharedDataSvc", [ function (){
return{
getValue: function(){
return "oioioioi";
}
}
}])
each controller may be instantiated multiple time ( with each instance $scope is a different scope)
each service is instantiated only once and then cached ( the same instance injected everywhere)

Resources