I'm trying to write a simple unit test for a controller in my app, but Jasmine is throwing an 'Unknown provider' error. It's choking on a provider that I wrote to help with retrieving template urls. The provider is injected into a config function so I can use it in routes.js.
The specific error I am getting is: Error: Unknown provider: assetPathProvider
Here is my Karma config:
files: [
'vendor/assets/javascripts/jquery.js',
'vendor/assets/javascripts/angular.js',
'spec/javascripts/lib/angular/angular-mocks.js',
'vendor/assets/javascripts/angular-*.js',
'vendor/assets/javascripts/*.js',
'app/assets/javascripts/initialize.js',
'app/assets/javascripts/**/*.js',
'spec/javascripts/unit/**/*.js'
],
I initialize my app like so:
Viewfinder = angular.module('viewfinder', [
'ui.bootstrap',
'scroll',
'ngCookies',
'ngResource',
'chart',
'http-auth-interceptor',
'facebook-connect',
'twitter-connect',
'Alerts',
'smartTable.table',
'ngClipboard',
'angularFileUpload'
])
Here is the top of routes.js
Viewfinder.config(['$routeProvider', '$locationProvider', 'assetPathProvider', function($routeProvider, $locationProvider, assetPathProvider) {
The provider is used in retrieving the correct template location in routes.js
...
templateUrl: assetPathProvider.get('welcome/signed_in.html'),
....
Here is the provider itself:
Viewfinder.provider('assetPath', [function() {
this.get = function(path) {
if(angular.isDefined(gon.config.manifest)) {
return '/assets/' + gon.config.manifest[path]
} else {
return '/assets/' + path
}
}
this.$get = function() {
return {
get: this.get
}
}
}]);
I've dumbed down my spec to be as simple as possible, but I can't get past the Unknown provider error.
Here is the spec:
describe('OneSheetPackagesViewController', function() {
var $rootScope, $scope, $controller, message
beforeEach(function() {
module('viewfinder', function(assetPathProvider) {})
})
beforeEach(inject(function(_$rootScope_) {
message = 'hello'
}))
it("should successfully submit a comment", function() {
console.log(message)
expect(message).toBeDefined()
})
})
Viewfinder.config(['$routeProvider', '$locationProvider', 'assetPathProvider', function($routeProvider, $locationProvider, assetPathProvider)
will be executed before
Viewfinder.provider('assetPath'
This when config is being executed assetPathProvider is not available.
http://docs.angularjs.org/guide/module
Module Loading & Dependencies A module is a collection of
configuration and run blocks which get applied to the application
during the bootstrap process. In its simplest form the module consist
of collection of two kinds of blocks:
Configuration blocks - get executed during the provider registrations
and configuration phase. Only providers and constants can be injected
into configuration blocks. This is to prevent accidental instantiation
of services before they have been fully configured. Run blocks - get
executed after the injector is created and are used to kickstart the
application. Only instances and constants can be injected into run
blocks. This is to prevent further system configuration during
application run time.
Try writing the provider in another angular module and inject it as dependency to viewfinder. also remember to ensure that the file defining assetPath appears before the viewfinder in karma.conf
Related
i am new to angular js i am trying to inject factory in a config ,but it shows module not found
factory code
var app=angular.module('app',['ui.router','angular-jwt','ngResource','angularValidator','satellizer']);
app.factory('AUTH_SERVICE', function ($auth,$state,$q) {
var auth={};
auth.isloggedin=function() {
var defer = $q.defer();
if($auth.isAuthenticated()){
defer.resolve();
}
else
{
$state.go('login');
defer.reject();
}
return defer.promise;
}
return auth;
});
config code is
app.config(['AUTH_SERVICE','$authProvider','$stateProvider', '$urlRouterProvider',function(AUTH_SERVICE,$stateProvider, $urlRouterProvider,$authProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home',{
url:'/',
templateUrl:'pages/home.html'
})
.state('login',{
url:'/login',
templateUrl:'pages/login.html',
controller:'loginCtrl'
})
.state('register',{
url:'/register',
templateUrl:'pages/register.html',
controller:'registerCtrl'
})
.state('dashboard',{
url:'/Dashboard',
templateUrl:'pages/dashboard.html',
controller:'dashctrl',
resolve:{
skiplogin: _skiplogin
}
})
function _skiplogin(AUTH_SERVICE){
return AUTH_SERVICEAUTH_SERVICE.isloggedin();
}
}]);
but it shows error
angular.js:38Uncaught Error: [$injector:modulerr]
http://errors.angularjs.org/1.5.8/$injector/modulerr?p0=app&p1=Error%3A%20%…ocalhost%2Faccoex%2Fbower_components%2Fangular%2Fangular.min.js%3A39%3A319)
You can't inject factory object in the app.config because it can't be sure they have been loaded correctly..
In the app.config you can only inject Consts and Providers.
From the documentation
A module is a collection of configuration and run blocks which get
applied to the application during the bootstrap process. In its
simplest form the module consist of collection of two kinds of blocks:
Configuration blocks - get executed during the provider registrations
and configuration phase. Only providers and constants can be injected
into configuration blocks. This is to prevent accidental instantiation
of services before they have been fully configured.
Run blocks - get executed after the injector is created and are used
to kickstart the application. Only instances and constants can be
injected into run blocks. This is to prevent further system
configuration during application run time.
Take a look to provider here where you can configure a service in your app.config.
you can't inject factory in config as it loads after config
angularjs application loads as follows:
Run
Config
Instantiate factory or service
Controller
To add dependency to Angular, this is what docs tells to do :
Source
//inject directives and services.
var app = angular.module('fileUpload', ['ngFileUpload']);
app.controller('MyCtrl', ['$scope', 'Upload', function ($scope, Upload) {...
But can we inject directive directly into controller like this:
var app = angular.module('fileUpload', []);
app.controller('MyCtrl', ['ngFileUpload','$scope', 'Upload', function (ngFileUpload,$scope, Upload) {...
If not, what is the reason for not providing this capability to controller?
Is there a way to inject dependency when a particular controller loads?
No modules are the essentially the base or a "toolbox" if you will. You need to inject one module into another so that the it can have access to its "tools" (filters/directives/services etc). The reason is that the module is responsible for dependency loading and order of operations. This is so that when you request a "tool" from a module into a controller you can be sure it is there (or an error will be thrown).
Modules can list other modules as their dependencies. Depending on a module implies that the required module needs to be loaded before the requiring module is loaded. In other words the configuration blocks of the required modules execute before the configuration blocks of the requiring module. The same is true for the run blocks. Each module can only be loaded once, even if multiple other modules require it.
When you inject one module into another you are saying "Module A requires things from Module B". Now when you when you require a specific tool that is when you inject it into the controller so that you have access to that specific tool.
So consider:
var app = angular.module('myApp', ['ngFileUpload']);
app.controller('MyCtrl', ['$scope', 'Upload', function ($scope, Upload) {
.....
Upload.upload(uploadData).then(function (resp) {
//success
}, null, function (evt) {
//upload progress
});
.....
}]);
So because you inject ngFileUpload your controller in the myApp module can now inject the Upload service from the ngFileUpload module and the controller does not need to worry if the service is present (if it is not you will get an injection error from angular).
Some I'm new to routing and single page web apps, but I've been trying to learn Angular correctly. There's some trouble I'm experiencing with it however and a few weird questions. I followed a guide on structuring your directory and mine looks something like this:
app/
components/
profile/
profile.html
ProfileModel.js
ProfileController.js
ProfileFactory.js
app.module.js
app.routes.js
My main module is located in app.module.js and is dependency injected with ngRoute and profile.app (the module for profile view from ProfileModel.js). It is declared like this:
angular
.module('app', ['ngRoute', 'profile.app'])
.controller('MainController', MainController);
function MainController() {
this.message = 'This is the home page';
}
Then in my app.routes.js file, I have declared all the routes the applications needs (so far only one, which is profile):
angular
.module('app')
.config(routeConfig);
routeConfig.$inject = ['$locationProvider', '$routeProvider'];
function routeConfig ($locationProvider, $routeProvider) {
$routeProvider
.when('/user/:user_id', {
templateUrl: '/app/components/profile/profile.html',
controller: 'ProfileController',
controllerAs: 'profile'
}
)
$locationProvider.html5Mode({
enabled: true,
requireBase: false
});
}
This is my ProfileController.js:
angular
.module('app.profile')
.controller('ProfileController', ProfileController);
ProfileController.$inject = ['ProfileFactory', '$routeParams'];
function ProfileController(ProfileFactory, $routeParams) {
var vm = this;
vm.user_id = $routeParams.user;
console.log($routeParams.user_id);
vm = ProfileFactory.userProfile(vm.user_id); //Gets the profile of the user and sets to to vm
}
So I have two main questions. $routeParams.user_id is logged as nothing despite I have defined the route in app.routes.js. This is weird because I have an ng-view directive in my index.html (the HTML file that includes every single .js file). Which means that I should have immediate access to the routing parameters once the controller and its dependencies are instantiated. However, when I go to http://example.com/user/1, I get nothing logged (undefined).
My second question is I included ngRoute as a dependency in profile.app and my main module (in which profile.app is a dependency). However, I later removed ngRoute from profile.app as a dependency, yet I left the injection of $routeParams inside my ProfileController and AngularJS didn't complain at all. This seems weird because the dependency is no longer explicitly present inside profile.app. So how come I can still seemingly inject $routeParams despite not having ngRoute in my profile.app module? Is it because it is getting ngRoute from the main module?
I believe the problem is that you are setting controllerAs to 'profile' but then in the controller you write var vm = this;
These two need to be the same so you could write controllerAs: 'vm', or var profile = this;
I have been playing around with some angular code and I found that it has app.controllers twice on the page. Once in the module of app towards the top and once at the bottom. But when I remove one or the other the code breaks. I don't know why you need both since the app.services doesn't need that or the directives or filters. Any insights on why?
(function () {
angular
.module('app',
[
'app.controllers',
'app.services',
'app.directives',
'app.filters'
]
)
.config(['$sceDelegateProvider','$routeProvider',
function ($sceDelegateProvider, $routeProvider) {
$sceDelegateProvider.resourceUrlWhitelist([
'self',
'https://maps.google.com/**']);
$routeProvider
// Home
.when('/',
{
templateUrl: 'partials/home.html'
}
)
// Default
.otherwise('/');
}
]
);
angular.module('app.controllers', []);
}());
This code :
.module('app', [
'app.controllers',
'app.services',
'app.directives',
'app.filters'
]);
is creating a new module named app. Inside [] you will find the list of dependencies modules. app.controllers is one of your app dependencie.
Whereas this code :
angular.module('app.controllers', []);
is creating a new module called app.controllers with no dependencies => [] (empty array).
To summarize
To create a new module
angular.module('MODULE_NAME', []); (Note there are [])
To access the module previously created
angular.module('MODULE_NAME');
Convention name xx.yy (like app.controllers) helps to know that the module xx.yy depends of xx (app.controllers is a dependency of app)
I am having some trouble injecting kinvey into my angular application. I have been getting the following error with the code below: Uncaught Error: [$injector:unpr]
var app = angular.module('FantasySeasons', ['snap', 'ngRoute', 'ngResource', 'ngTouch',
'angular-carousel', 'FSControllers', 'FSPartials', 'kinvey']);
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/', {
templateUrl : 'partials/home.html',
controller : 'HomeCtrl'
}).otherwise({
redirectTo : '/'
});
}]);
app.run(function($kinvey){
var promise = $kinvey.init({
appKey: 'your app key',
appSecret: 'your app secret'
});
});
As the creator of the Kinvey Angular library, I don’t see anything wrong with declaring the Kinvey dependency.
Since the error message is not specific to any module, I can only guess at this point. One thing to note is the ngRoute module is no longer part of the AngularJS core (since 1.2.0). To use it, you need to include it separately, see docs. Make sure this is the case. Otherwise, you run into that error.