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.
Related
I'm using Webpack 1.13.2 with Angular 1.5.8 and I can't manage to access my "SETTINGS" constant from my poiService file.
TypeError: Cannot read property 'API_URL' of undefined
http://plnkr.co/edit/6repllAk39kv4Enfw8RU?p=catalogue
Thanks for help.
PoiService has mismatching annotation:
services.factory('PoiService', ['SETTINGS', require('./poiService')])
service definition and
module.exports = function ($http, SETTINGS) { ... }
function signature.
For this reason it may be not advisable to keep factory function and service definition in separate files. And even if there's a need to do this, it is preferable to use named function and $inject annotation instead of inline array annotation:
services.factory('PoiService', require('./poiService'))
...
poiService.$inject = ['$http', 'SETTINGS'];
function poiService($http, SETTINGS) { ... }
module.exports = poiService;
I am trying to inject a custom factory from one module into a custom provider from another module. What I really want to do is to use the custom factory inside the config block but I can't, so I'm trying to configure a provider that uses the custom factory which will then get injected into config.
I have been trying to inject my custom factory into the provider but I can't seem to get it right. I don't know if it's syntax or maybe my approach is wrong. My questions are:
1.) is this even possible?
2.) is my syntax correct?
Here is the factory:
.factory('myFactory', myFactory);
myFactory.$inject = ['$q', '$http', 'Story'];
function myFactory($q, $http) {
return {
getSomething: getSomething,
}
function getSomething() {
}
}
Here is the provider:
.provider('myProvider', function() {
return {
$get: function(myFactory) {
function getStuff() {
return myFactory.getSomething().then(function(data){
return data;
})
}
return {
stuff: getStuff
}
}
}
})
The error that I am getting is this:
Cannot read property 'getSomething' of undefined
Is this the correct use of a provider? I feel like I may be missing something. Thanks!
During the configuration phase, you can't access services:
During application bootstrap, before Angular goes off creating all
services, it configures and instantiates all providers. We call this
the configuration phase of the application life-cycle. During this
phase, services aren't accessible because they haven't been created
yet.
https://docs.angularjs.org/guide/providers
I have two directives: A and B. They're very similar. I want directive B to inherit the controller in directive A.
In other words, the same function used for controller: in A's directive definition object needs to also be the controller: function used in B's directive definition object.
Aside from a copy/paste of the controller: function, how I use the same function in both A and B's definition?
Controllers are just regular JS functions, so, you can use prototyping:
function BaseController(){
this.commonFunct = function(){
...
}
}
function CtrlA(){
}
CtrlA.prototype = BaseController
function CtrlB(){
}
CtrlB.prototype = BaseController
this works with controllerAs syntax, when your controller is exposed to scope under some name, say ctrl. Then $scope.ctrl.commonFunct (more generic, works from any place of controller) or this.commonFunct (can be used in controller's instance methods, where this is controller itself) can be used to refer the function.
That works if you declare both controllers in one module as named functions. If they are declared in different modules, you can use mixin-like way with $controller:
// Base module
(function() {
'use strict';
angular.module('Base', []);
function BaseController($scope, <injectables>, that) {
that.commonFunct = function() {
};
}
angular.module('Base').controller('BaseController',
['$scope', '...', BaseController]);
})();
// Module that inherits functionality
(function() {
'use strict';
angular.module('Derived', ['Base']);
function DerivedController($scope, $controller, ...) {
$controller('BaseController', {
'$scope' : $scope,
...
'that' : this
});
// this.commonFunct is available
}
angular.module('Derived').controller('DerivedController',
['$scope', '$controller', '...', DerivedController]);
})();
MHO: I suggest to use named functions for declaring controllers / services and directives as it is more natural, JS way of doing things. Also, I like controllerAs syntax much as it helps to distinguish data, stored directly in scope (like $scope.data) with controller's methods (they all are stored in one scope's named object, like $scope.ctrl).
If I understand correct, you don't really want to inherit controllers. You want to use one controller in 2 different directives.
If that's the case, just declare the controller function, and pass it to both directive definition objects as a function or a string.
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/
I have a route provider with following configuration.
ocsApp.config(function($routeProvider){
$routeProvider
.when('/userStat/:pageNo' ,
{
templateUrl:'userStats',
controller:'userStatController',
resolve: {
loadData: searchController.loadData
}
});
});
The loadData function i.e resolve method calls a service which needs around 6-7 parameters that I do not want to appear in route URL and all these are available in searchController scope. Is there a way i can pass those parameters in loadData function.
Code is like this.
searchController.loadData=function($q,sessionFactory,$route){
var deferred = $q.defer();
sessionFactory.getSessions(siteId,startDate,endDate,pageNo,resultPerPage)
success(function(data,status)
{
deferred.resolve(data);
});
return deferred.promise;
};
is there a way to pass those parameters in loadData function.
The "searchController" scope doesn't really exist in the context you're thinking of.
What you should do is abstract the parameters currently in the searchController scope into a service, put your parameters there, and inject that into your loaddata function. If binding is important, you will either have to manually update that service's parameters based on $watch or you can put an object of parameters from the service into the searchController scope and regular binding will work.