$controller not getting instantiated in jasmine - angularjs

This snippet of code is giving error when running my test case.
beforeEach(module(function($scope) {
// do something interesting with the service
//console.log($controller);
}));
Error coming up in console.
debug.js:21 Uncaught Error: [$injector:modulerr] Failed to instantiate
module function ($scope) due to:
Error: [$injector:unpr] Unknown provider: $scope
Could anyone please explain why it is not able to find $scope. It is also not working if I change it to $timeout or $controller.
Updated Code
If I change the code to this
Then it works
module(function($controllerProvider) {
// we could also access the $controllerProvider.allowGlobals() function,
// which allows us to register a controller on the window object.
$controllerProvider.register('ProductsController', function() {
// logic of the controller...
});
});

You're mixing up two different functions module and inject.
module configures the jasmine environment to use the angular module you specify. That's probably app. If you don't call this, the injector will not be able to find your services, controllers, directives, etc. In the module function, you can inject providers, not services, factories, etc. You use providers to configure how the services will behave later.
inject takes the function you supply and "injects" the services, constants, factories, etc by name before calling it. You use this to inejct the services etc themselves.
Here's a sample of how to split the calls in your code. I also changed the $scope to use $rootScope and create a scope.
beforeEach(function() {
module("app"); //or if your module is name something different, use that name instead
inject(function($controller, $rootScope) {
var $scope = $rootScope.$new();
var myCtrl = $controller("myCtrl", { $scope: $scope });
}));
});

Related

Angular JS - Is it possible to create factory or service with $provider other than in config()

I have couple of question
Angular JS - Is it possible to create factory or service with $provider other than in config()
Is it possible to create $Provider instance with injector
i,e angular.injector(['ng']).get("$provider"); - When I tried it was throwing error
Thanks in advance
Updating the question for more clarity
Actually my question was- is it possible to create a factory/service by the injector $provider. I know the difference between factory , service and provider.And also I know we can create a factory inside config() like
app.config(function($provide){
$provide.factory('newFac',function(){
return {
newT : 'This is from confing()'
};
});
});
Just curious to know whether is it possible to create factory outside config() with $provide
Angular JS - Is it possible to create factory or service with $provider other than in config()
Not clear what you are asking, but let me explain a bit. Only providers can be injected into config phase, nothing else, but the difference between provider/service/factory is not so much big, so basically you could write any factory, and inject into config, but you must name it correctly, and provide a method called $get this way angular knows what object exposes your factory into config. Don't get wrong, only providers can be injected into config, but you could write your factory as it's a provider.
Here's example.
var app = angular.module('test', []);
//simple service
app.service('service1', function(){
//some stuff
});
//simple provider
app.provider('service2', function(){
//some stuff
this.$get = function(){ //now this provider can be injected into config
return this; //there are only 2 differences, `app.provider` and `this.$get` method
}
});
app.config(function(service2Provider){ //notice `Provider` prefix
//some stuff
});
angular.injector(['ng']).get("$provider");
This will work on your module, for example, your provider is TestProvider, and the module that creates this provider is TestModule, then you must use
angular.injector(['TestModule']).get("TestProvider");
Also it works for services, constants, values, factories

$provide outside config blocks

I'm certainly missing some fundamental point about the injector, but I fail to understand why exactly this
angular.module('app').config(function ($provide) {
...
});
and this
angular.module('app').config(function ($injector) {
$injector.invoke(function ($provide) { ... });
});
work as intended, while this
app.run(function($provide) {
...
});
will throw
Error: [$injector:unpr] Unknown provider: $provideProvider <- $provide
As follows from the above, config has some special relationship with providers, while run deals with instances, yet I'm unsure about the thing that makes config blocks so special.
As a consequence of that, is there no way to get to $provide outside config blocks, e.g. with angular.injector() (though it seems that it gets provider instances also)?
The question, besides mere curiosity, also has some practical considerations. In 1.4 all of $provide functions are exposed to module, but that's not true for 1.3.
The purpose of the config() function is to allow you to perform some global configuration that will affect the entire application - that includes services, directives, controllers, etc. Because of that, the config() block must run before anything else. But, you still need a way to perform the aforementioned configuration and make it available to the rest of the app. And the way to do that is by using providers.
What makes providers "special" is that they have two initialization parts, and one of them is directly related to the config() block. Take a look at the following code:
app.provider('myService', function() {
var self = {};
this.setSomeGlobalProperty = function(value) {
self.someGlobalProperty = value;
};
this.$get = function(someDependency) {
this.doSomething = function() {
console.log(self.someGlobalProperty);
};
};
});
app.config(function(myServiceProvider) {
myServiceProvider.setSomeGlobalProperty('foobar');
});
app.controller('MyCtrl', function(myService) {
myService.doSomething();
});
When you inject a provider into the config() function, you can access anything but the $get function (technically you can access the $get function, but calling it won't work). That way you can do whatever configuration you might need to do. That's the first initialization part. It's worth mentioning that even though our service is called myService, you need to use the suffix Provider here.
But when you inject the same provider into any other place, Angular calls the $get() function and injects whatever it returns. That's the second initialization part. In this case, the provider behaves just like an ordinary service.
Now about $provide and $injector. Since they are "configuration services", it makes sense to me that you can't access them outside the config() block. If you could, then you would be able to, say, create a factory after it had been used by another service.
Finally, I haven't played with v1.4 yet, so I have no idea why that behavior apparently has changed. If anyone knows why, please let me know and I'll update my answer.
After some Angular injector study I was able to give an exhaustive answer to my own question.
Essentially, $injector in config blocks and provider constructor functions and $injector everywhere else are two different services with the same name, which are defined on internal provider/instance cache explicitly, together with $provide (this one is being defined in provider cache, hence it can be injected in config only).
While generally not recommended because of probable race conditions, it is possible to expose internal services to instance cache and make config-specific $provide and $injector available for injection after config phase has ended:
app.config(function ($provide, $injector) {
$provide.value('$providerInjector', $injector);
$provide.value('$provide', $provide);
});
The possible applications are configuring service providers any time (if possible)
app.run(function ($providerInjector) {
var $compileProvider = $providerInjector.get('$compileProvider');
...
});
and defining new components at run-time
app.run(function ($provide) {
$provide.controller(...);
...
});

How can I retrieve the injector for my application?

I'm trying to run a function and have services injected into it. I thought this could easily be accomplished using $injector. So I tried the following (simplified example):
angular.injector().invoke( [ "$q", function( $q ) { $q.something(); } ] );
Which results in Uncaught Error: [$injector:unpr] Unknown provider: $qProvider <- $q.
I know I can solve that by using angular.injector( ["ng"] ) instead, but there are actually several more dependencies.
It would be perfectly fine, if I could just retrieve the injector instance that is used everywhere else in my application.
The documentation for angular.injector suggests that you can retrieve it with angular.element(document).injector(), but that results in undefined for me.
You shouldn't be needing this, but you can get your app's $injector using the root-element of your app (or any child element).
E.g., if you use ngApp on the body:
angular.element(document.body).injector();
ExpertSystem's answer worked perfectly for me
This works perfectly for me. I'm trying to invoke a function from an angular service outside the angular scope in the document "resume" event for use in my cordova application. Here is the code that I used
var injector = angular.element(document.body).injector(); //get the document
injector.invoke(['myService', function (myService) {
myService.doSomething();
}]);
Try this:
var $injector = angular.injector(['myApp','ng'])
For $location you need to bootstrap the app to the page (start the app):
var $injector = angular.bootstrap(document, ['myApp'])
This will return the injector every time you call it but won't create any conflicts if the script was already loaded.

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 :-)

AngularJS: how to "create" controller in unit test, and why scope is not defined?

everywhere in application my company created we used this example of how to create a controller :
app.myFunnyController = function($scope.....){}
but i see that everywhere in test people are using this way of creating controllers:
app.controller('myFunnyController', function ($scope) {
}
And i can see that when i am creating my test and using app.myFunnyController declaration:
'use strict';
describe('publicCtrl', function(){
beforeEach(module('app'));
it("should be true", inject(function($controller){
var scope = {},
ctrl = $controller('myFunnyController', {$scope : scope});
expect(scope.data).toBe("test2");
}));
})
I getting an error of myFunnyController is not a function. If i using the second type of declaration, everything works fine. Why does this happend?
An other problem is that i am getting error: scope is not defined.
I am new to Karma and Unit testing for front end, what am i doing wrong?
From my understanding, the second syntax (app.controller(...)) registers the controller function on the module. The first syntax just adds the controller function as an attribute of the module. app.controller does a bit more magic under the hood so that when you call $controller('myFunnyController, ...) in your test, the module knows about the controller and can run it. This is the recommended way to define controllers according to the angular controllers guide.

Resources