I have a factory defined as app.factory('MyFactor'), and I want to inject this into the .run() of my main module.
I tried the same way I inject dependencies into a directive:
app.run(['MyFactory', function(MyFactory)
{
}]);
But I get an error say that this is an unknown Provider. What's wrong?
Injecting instances into a run function works. There were two wrong answers to this question claiming it doesn't.
Consider this:
angular.module('app',[])
.factory('myFactory', function() {
return {
foo: function() { return 'bar' }
};
})
.run(['myFactory', function(myFactory) {
alert(myFactory.foo());
}]);
It runs without errors and alerts the result from invoking a function on the myFactory service
(yes it's still a service even if you call it a factory).
Most likely your error is caused by a misspelling of the name. In your posted code you have app.factory('MyFactor') which is missing a trailing "y".
JSFIDDLE: http://jsfiddle.net/os4erzjx/
Related
I'm trying to write unit tests for an angular service with jasmine/karma. I have a similar service test, which works just fine. But this one has some additional dependencies, is in a different module and just doesn't find the service with the inject.
The service looks something like this. bService is in the same module, but commonFactory and commonService are in another module, say commonModule.
(function () {
'use strict';
angular
.module('myService')
.service('aService', aService);
aService.$inject = [
'commonFactory',
'commonService'
'bService'
];
function aService (
commonFactory,
commonService,
bService
) {
};
return {
codeIWantToTest: CodeIWantToTest;
}
function CodeIWantToTest () {
console.log('surprise!');
}
})();
My jasmine test looks like:
describe('myService.aService', function () {
'use strict';
var aService;
// I tried adding beforeEach(module('commonModule')); too, but that didn't do anything
beforeEach(module('myService'));
beforeEach(function() {
inject(function(_aService_) {
console.log('getting aService');
aService = _aService_;
});
});
it('tests my service is defined', function() {
expect(myService).toBeDefined();
});
});
This test fails. myService isn't defined and the console.log in the inject function doesn't ever fire. My karma.conf.js basically lists the dependencies in the order that they're injected into the service, then adds the service then the test.
What would cause the inject to not grab the service? What am I missing? I mentioned I have a similar test for commonService and it works just fine. So I'm baffled.
Another dev on my team found the solution and I wanted to post it as an answer for the future people. I had a feeling it was a dependency problem, and it was. While we were loading all of the JS stuff correctly, the template that the component uses was loading another js dependency. So to fix this for jasmine, we had two different solutions:
at the top of the component test file, we could add:
beforeEach(function () {
module(function ($provide) {
$provide.constant('myMissingDependency', {
// do things to load the dependency here
});
});
});
In our case it was a translation library
The other solution was to add a 'shim' file into the unit test directory and load it with karma.config.js ahead of the tests. That looked like:
(function() {
angular
.module('MyService')
.constant('myMissingDependency', Object.freeze({
// things to load the dependency
}));
})();
I wasn't able to switch to Chrome because we're using Docker and I couldn't get the tests to run locally to run Chrome. So adding a second set of eyes to this was what I needed.
I need to modify a constant service. This service has no tests attached, but I wanted to, at least partially, cover the file and my modifications with unit tests that I will write with Jasmine.
The service looks like:
(function () {
angular
.module('app')
.constant('myService', myService);
function myService() {
dependencyOne: dependencyOneImpl,
someFunction: someFunctionImpl
...
}
dependencyOneImpl.$inject = ['someDependency'];
function dependencyOneImpl(someDependency) {
...
}
someFunctionImpl.$inject = ['dependencyOne', 'dependencyTwo'];
function someFunctionImpl(dependencyOne, dependencyTwo) {
...
}
})();
It is used in the resolution process of routes (we use ui-router). When a particular route is activated, a function of the service is invoked:
someRoute = {
url: '...',
name: '...'
views: { ... },
resolve: { resolver.someFunction }
}
There are no dependencies required in the construction of the service, and I'm trying to test one of the public functions that the service exposes.
Typically I would write the test like:
prepareSomeSetup();
theService.someFunction();
assertSomething();
I could manually resolve the dependencies required by the function, but I would like to know if I can get the same behavior that ui-router is getting when resolving the function. The dependencies seem to be resolved and the function is executed.
It crossed my mind that I probably needed to inject $injector service in the test file and manually call invoke somehow to resolve the function, but it does not seem to work.
How can I call the function with the two dependencies resolved?
When you define an injectable function you have to call it using $injector.invoke(fn, scope, otherArgs);
Where
fn the injectable function, or array for inline injection
scope : the "this" of the function
otherArgs : others Arguments that are not defined as service,... in angular that you inject.
I'm looking for a way to pass a value to my controller from my appRoutes. With the idea for it to call a function and do some magic. heres some code so you get an idea:
appRoutes.js
$routeProvider
.when('/students/some/path/:id', {
templateUrl: 'views/studentRecord.html',
controller: 'StudentsController',
resolve: { myVar: 'test' }
});
studentsCtrl.js
angular.module('StudentsCtrl', [])
.controller('StudentsController', function($scope, $http, $routeParams,
$location, myVar, Students) {
/* ... */
});
Ideally, I'd like to call a given function within this controller - but parsing a value would be just as good. The idea is that the controller handles all pages to do with 'students' and will make some http calls so my node server will do some calls to mongodb. Ive tried a few variations on the internet and with no luck. I got an error:
Error: $injector:unpr Unknown Provider
but I'm not sure how to resolve it.
EDIT: I've half resolved this now by using this; http://plnkr.co/edit/mSb58e8cGDNYU27xSizk?p=preview
ideally i'd like to separate the app.js into controllers and services - currently working on this, any edit of the plnkr would be great.
Question still stands of is it possible to hit a function within the controller first, rather than resolving one through a service?
In each resolve property, you can have a function that lets Angular inject services for you to use:
resolve: {
myVar1: function(testService) { return testService.fetchList1(); },
myVar2: function(anotherService, $http) {
// call service functions to mongo-db or what have you
return result;
}
}
Then, your controller, just inject the properties:
// myVar1 and myVar2 are now here
app.controller('StudentsController', function($scope, myVar1, myVar2) {
/* ... */
});
If the returned value from the function inside resolve is a promise, then it will be resolved before controller code is called (hence, the name resolve). This is actually the recommended approach as it makes service code (such as testService) reusable across many controllers.
Passing a function that returns the value
.state('tab2', {
url: '/tab2',
templateUrl: 'templates/tab2.html',
controller: 'Tab2Controller as tab2Ctrl',
//promise
resolve: {
lastName: function () { return 'makarov'}
}
});
Then in the controller
function Tab2Controller(lastName) {
console.log("Tab2", lastName);
}
I am new to angular js..I am getting follwing error please help me out
[ng:areq] Argument 'fn' is not a function, got string
var app = angular.module('demo',[]);
app.config('$routeProvider',function($routeProvider){
$routeProvider.when('/add',{
templateUrl:'demo/add.html',
controller:'addcontroller'
}).
when('/order',{
templateUrl:'demo/order.html',
controller:'ordercontroller'
});
});
app.controller('addcontroller',function($scope){
$scope.message="order";
});
app.controller('ordercontroller',function($scope){
$scope.message="order";
});
I think the error is in the config block, it should either be:
app.config(function($routeProvider){
// routeProvider config
});
or better:
app.config(['$routeProvider', function($routeProvider){
// routeProvider config, allows minification
}]);
the annotations are there for minification to work correctly. You can read more about it on AngularJS docs https://docs.angularjs.org/tutorial/step_05
Please note that this practice needs to be done throughout the app to work correctly.
Although not directly related to the context of this question, this error message can also be caused by a resolve block returning something else than a function, like this:
$stateProvider.state('mystate', {
url: "/myurl",
templateUrl: "mytemplate.html",
controller: "MyController",
resolve: {
authenticatedUser: securityAuthorizationProvider.requireAuthenticatedUser
}
});
if securityAuthorizationProvider.requireAuthenticatedUser happens to be undefined, you can get this error.
I know this is an older post, but thought I'd contribute what was wrong with my code with this exact error. I was injecting a service into my controller this way:
theApp.directive('theDirective', 'myService', ['$http', function ($http, myService) {}]);
Instead of this:
theApp.directive('theDirective', ['$http', 'myService', function ($http, myService) {}]);
Notice that my service was included outside of the inline array annotation! It was a boneheaded move on my part that cost me way too much time.
For the the problem was defining factory dependency outside the array. Most probably the minification was also an issue.
//Error in this
angular
.module('mymodule').factory('myFactory', 'thisconstant', function (thisconstant) {
});
This fixed by
//correct code
angular
.module('mymodule').factory('myFactory', ['thisconstant', function (thisconstant) {
}]);
Since there's been various use cases added here, I thought I'd also add mine. I got this error when re-formating a function service
angular
.app('myApp')
.service('MyService', function (){
// do something
return MyService;
})
into a class object:
export default class MyService { ... }
angular
.app('myApp')
.service('MyService', MyService);
My problem was that my service was returning itself at the end of the function and I needed to add a class function, which would do this:
export default class MyService {
constructor(){
// doing something
}
get() {
return MyService;
}
}
which fixed the issue for me. :)
I got this error by misunderstanding the way anonymous functions and named functions are treated in JavaScript.
This causes the error:
angular.module("app").factory("myDataService", ['$resource', myDataService]);
var myDataService = function($resource) {
return $resource('/url');
}
I should've either done this:
var myDataService = function($resource) {
return $resource('/url');
}
angular.module("app").factory("myDataService", ['$resource', myDataService]);
Or this:
angular.module("app").factory("myDataService", ['$resource', myDataService]);
function myDataService($resource) {
return $resource('/url');
}
More on the difference between anonymous function and named function here
In First line you need to use like this,
var app = angular.module('demo', ['ngRoute']);
and use the routing thing like this,
$routeProvider.when('/add').then({
templateUrl:'demo/add.html',
controller:'addcontroller'
});
Just try these steps once.
Is it possible to do DI in a provider method?
In this example
angular.module('greet',[])
.provider('greeter',function() {
this.$get=function() {
};
})
.service('greeterService',function($http){
console.log($http);
})
;
Injecting $http into service appears to be the correct implementation, but it doesn't work in a provider method and it throws an error:
Unknown provider: $http
Does the provider method work with DI to inject services?
You can certainly inject $http to provider. Just make sure it appears in $get, not the function constructor. As follows:
angular.module('greet',[]).provider('greeter',function() {
this.$get = function($http) {
};
});
You can inject constants and other providers into a provider. Not services or factories - with one exception. It seems that you can inject the $injector service into a provider - at least, you can in AngularJS 1.3.16.
.provider('foo', ['$injector', function ($injector) {
var messagePrefix = $injector.get('msgPrefix');
this.message = '';
this.$get = function() {
var that = this;
return function() {
return messagePrefix + that.message;
}
};
}])
You can use the injector outside the $get method, but you still can't get services from it at configure time.
See here for a demo.
Following up on IgrCndd's answer, here's a pattern that might avoid potential nastiness:
angular.module('greet',[]).provider('greeter', function() {
var $http;
function logIt() {
console.log($http);
}
this.$get = ['$http', function(_$http_) {
$http = _$http_;
return {
logIt: logIt
};
}];
});
Note how similar this is to the equivalent service, making conversion between the two less troublesome:
angular.module('greet',[]).factory('greeter', ['$http', function($http) {
function logIt() {
console.log($http);
}
return {
logIt: logIt
};
});
You actually have to inject the dependency on $get and then store it to use on what you retrieve from $get. Not beautiful at all...
No, you can not inject a service into the provider itself.
Injecting a service into a provider's $get method is the same as injecting a service into a factory, but you can not inject it into the provider function directly.
The difference between $get and the provider itself is that the provider runs during the module loading phase whereas the $get is run when instantiating the service you are providing.
This implies that you can not use any service at all during the module loading/configuration phase of your modules. That is all the stuff you run inside your config blocks, such as when defining your app routes or states, can not make use of any service.
The only other thing you can inject into config blocks besides providers are constants.
You could do something like IgrCndd suggested. But if you needed to consume the provider in a config block, which is the provider's purpose after all, you will not have your values injected until much after. So it's not going to work unless you do some nasty hack using promises.
Further reading on injectables