I'm using Brian Ford's angular-modal library to provide modals for my app. It works properly in development, but minification seems to break it even though I'm using ngmin through a gulp build process.
Error: [$injector:unpr] Unknown provider: tProvider <- t <- btfModal <- deleteConfirmationModal
http://errors.angularjs.org/1.2.17-build.225+sha.9227a5d/$injector/unpr?p0=tProvider%20%3C-%20t%20%3C-%20btfModal%20%3C-NaNeleteConfirmationModal
at http://localhost:3005/live/vendors.min.js:1:697
at http://localhost:3005/live/vendors.min.js:1:17123
at Object.n [as get] (http://localhost:3005/live/vendors.min.js:1:16388)
at http://localhost:3005/live/vendors.min.js:1:17218
at n (http://localhost:3005/live/vendors.min.js:1:16388)
at Object.r [as invoke] (http://localhost:3005/live/vendors.min.js:1:16673)
at http://localhost:3005/live/vendors.min.js:1:17236
at n (http://localhost:3005/live/vendors.min.js:1:16388)
at Object.r [as invoke] (http://localhost:3005/live/vendors.min.js:1:16673)
at http://localhost:3005/live/vendors.min.js:1:17236
Based on what I've read in general about Unknown provider errors, I'm sure it’s a problem with dependency injection. I've tried every permutation I can think of, but I can't make it work.
Here's the pertinent code:
Creates the module:
angular.module('gridsmartWebClient.modal', ['btford.modal']);
Factory:
angular.
module('gridsmartWebClient.modal')
.factory('deleteConfirmationModal', function (btfModal) {
'use strict';
return btfModal({
controller: 'DeleteModalCtrl',
controllerAs: 'modal',
templateUrl: '/gridsmart-web-client/camera/delete-confirmation.html'
});
})
Factory after ngmin:
angular.module('gridsmartWebClient.modal').factory('deleteConfirmationModal', [
'btfModal',
function (btfModal) {
'use strict';
return btfModal({
controller: 'DeleteModalCtrl',
controllerAs: 'modal',
templateUrl: '/gridsmart-web-client/camera/delete-confirmation.html'
});
}
])
App dependencies:
angular.module('gridsmartWebClient', [
'ngRoute',
'gridsmartWebClient.grid',
'gridsmartWebClient.camera',
'gridsmartWebClient.modal',
'gridsmartWebClient.utils',
'dropdownDirective',
'btford.modal',
'gridsmart-web-client-templates'
])
So, despite the fact I know there's a problem with dependency injections, I can't see what it would be. The module the factory is declared in has been injected in the app. I've looked through other responses to similar questions but answers are either very specific to the respective scenarios or general so as not to be especially useful to me. (e.g. "It's a dependency problem."). What gives?
The problem is in vendors.min.js, not your code. You need ngmin all your vendor scripts as well.
Related
So I am trying to jump on the Angular bandwagon, and I have been tasked with building an SPA, for which I have selected AngularJS with ASP.Net MVC Web API (I am a .Net developer). As a fan of strongly typed languages, I have avoided javascript whenever possible throughout my career, but frameworks like AngularJS and the other libraries & plugins in recent years have made it impossible to ignore. So here I am, asking for some guidance.
I have watched the tutorials, done the sample code projects and done some learning on PluralSight, and I have things working, at least from a foundational perspective. I have a rich background in MVVM and MVC, so SOC is a big thing for me. I like the MVC type of structure that Angular provides, which is largely why I went this route in the first place.
Now let me get to my issue(s). I am initializing my module (currently) in my master page (_Layout.cshtml), which I did while tweaking and experimenting, for the sake of simplicity.
<script>
angular.module('xcmApp', ['ngRoute', 'ngResource'])
.config(function ($routeProvider) {
$routeProvider
.when('/',
{
controller: 'companiesController',
templateUrl: 'views/companylist.html'
})
.when('/Reports',
{
controller: 'reportsController',
templateUrl: 'views/reportlist.html'
})
.otherwise({ redirectTo: '/' })
})
.factory('companiesFactory', ['$resource',
function ($resource) {
return $resource('/api/companies', {}, {
query: { method: 'GET', params: {}, isArray: true }
});
}
])
.controller('companiesController', function ($scope, companiesFactory) {
$scope.Companies = companiesFactory.query();
});
</script>
But now that I am ready to move on to deeper concepts, I want to break my scripts out into their appropriate files. Namely, app.js and associated controllers/factories/services etc. However when I move that script into app.js and reference it in _Layout.cshtml, it errors:
<script src="~/app.js"></script>
Error: [$injector:unpr] Unknown provider: a
Now there's no point in continuing to break out into controller files etc. when I can't even get the app.js to work right, so here I am stuck. I know there are some brilliant AngularJS devs on here that probably know what I'm missing before even reading this far, and I am grateful for your assistance.
Anyone who can highlight my oversight will be a superstar for me today. Thanks in advance!
EDIT:
Here is my Stack Trace:
0x800a139e - JavaScript runtime error: [$injector:modulerr] Failed to instantiate module xcmApp due to:
Error: [$injector:unpr] Unknown provider: a
http://errors.angularjs.org/1.3.15/$injector/unpr?p0=a
at Anonymous function (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js:4015:13)
at getService (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js:4162:11)
at invoke (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js:4191:9)
at runInvokeQueue (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js:4109:11)
at Anonymous function (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js:4118:11)
at forEach (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js:323:11)
at loadModules (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js:4099:5)
at createInjector (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js:4025:3)
at doBootstrap (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js:1452:5)
at bootstrap (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js:1473:5)
http://errors.angularjs.org/1.3.15/$injector/modulerr?p0=xcmApp&p1=Error%3A%20%5B%24injector%3Aunpr%5D%20Unknown%20provider%3A%20a%0Ahttp%3A%2F%2Ferrors.angularjs.org%2F1.3.15%2F%24injector%2Funpr%3Fp0%3Da%0A%20%20%20at%20Anonymous%20function%20(http%3A%2F%2Fajax.googleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.3.15%2Fangular.js%3A4015%3A13)%0A%20%20%20at%20getService%20(http%3A%2F%2Fajax.googleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.3.15%2Fangular.js%3A4162%3A11)%0A%20%20%20at%20invoke%20(http%3A%2F%2Fajax.googleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.3.15%2Fangular.js%3A4191%3A9)%0A%20%20%20at%20runInvokeQueue%20(http%3A%2F%2Fajax.googleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.3.15%2Fangular.js%3A4109%3A11)%0A%20%20%20at%20Anonymous%20function%20(http%3A%2F%2Fajax.googleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.3.15%2Fangular.js%3A4118%3A11)%0A%20%20%20at%20forEach%20(http%3A%2F%2Fajax.googleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.3.15%2Fangular.js%3A323%3A11)%0A%20%20%20at%20loadModules%20(http%3A%2F%2Fajax.googleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.3.15%2Fangular.js%3A4099%3A5)%0A%20%20%20at%20createInjector%20(http%3A%2F%2Fajax.googleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.3.15%2Fangular.js%3A4025%3A3)%0A%20%20%20at%20doBootstrap%20(http%3A%2F%2Fajax.googleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.3.15%2Fangular.js%3A1452%3A5)%0A%20%20%20at%20bootstrap%20(http%3A%2F%2Fajax.googleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.3.15%2Fangular.js%3A1473%3A5)
REMINDER:
My problem is not that it doesn't work, it DOES WORK. It just STOPS working when I take the Javascript out of my HTML page and place it into a referenced app.js file.
Go back to the documentation. You are using a bad way of defining your application. Try:
var MyApp = angular.module( 'MyApp', ['ngRoute' , 'ngSanitize']) ;
and then use MyApp to add controllers, filters, etc.
EDIT:
BTW, it is a good practice to write the Javascripts in separate files for SPA (you are not writing a small project I guess).
It looks like you are missing a few declaration pieces related to Dependency Injection in your controller definition.
Before I post the specific code and fixes, I want to mention a useful troubleshooting tool for Dependency Injection issues. Angular has a built in directive ng-strict-di. This directive is a companion to ng-app. From the ng-app documentation:
if this attribute is present on the app element, the injector will be created in "strict-di" mode. This means that the application will fail to invoke functions which do not use explicit function annotation (and are thus unsuitable for minification), as described in the Dependency Injection guide, and useful debugging info will assist in tracking down the root of these bugs.
Now, to the code in your post:
Your controller is not using explicit function annotation. In explicit function annotation, you pass a string array of dependencies, followed by the function. This ensures that even if minification were to rename the function parameters, Angular can still identify which dependency to supply to the function. You have used explicit annotation in parts of your code, but it is missing from your controller definition.
This is classically easy to identify, with Error: [$injector:unpr] Unknown provider: a, even though you never defined a provider named a. ng-strict-di would flag this controller code.
Here is the current code, and the proposed fix.
Instead of:
.controller('companiesController', function ($scope, companiesFactory) {
Try:
.controller('companiesController', ['$scope', 'companiesFactory', function ($scope, companiesFactory) {
I don't have an ASP.net background, but I think that souldn't matter to answer your Angular question.
First declare your modules like this:
angular.module('myModule', []);
and get them im oder files like this:
angular.module('myModule').controller/foactory/....
So your code should look something like this:
//xcmApp.module.js
angular.module('xcmApp', ['ngRoute', 'ngResource']);
//xcmApp.config.js
angular.module('xcmApp').config(function ($routeProvider) {
$routeProvider
.when('/',
{
controller: 'companiesController',
templateUrl: 'views/companylist.html'
})
.when('/Reports',
{
controller: 'reportsController',
templateUrl: 'views/reportlist.html'
})
.otherwise({redirectTo: '/'})
});
//xcmApp.factory.js
angular.module('xcmApp').factory('companiesFactory', ['$resource',
function ($resource) {
return $resource('/api/companies', {}, {
query: {method: 'GET', params: {}, isArray: true}
});
}
]);
//xcmApp.controller.js
angular.module('xcmApp').controller('companiesController', function ($scope, companiesFactory) {
$scope.Companies = companiesFactory.query();
});
EDIT:
Regarding the error, keep in mind that your dependencies must be in the rigth order.
So your index.html (assuming you use don't use a script loader yet) should look like this:
<script src="/*path to angular*/"></script>
<script src="/*path to ngRoute*/"></script>
<script src="/*path to ngResource*/"></script>
<script src="/*path to xcmApp.module.js*/"></script> //setting your app module must come first
<script src="/*path to xcmApp.config.js*/"></script>
<script src="/*path to xcmApp.factory.js*/"></script> //must come before the controller in your case
<script src="/*path to xcmApp.controller.js*/"></script>
I am trying to use this plugin https://github.com/jtblin/angular-chart.js.
And I am getting this error (I don't think the problem is with the plugin instead it's in the way I'm doing the injection!!):
Error: [$injector:unpr] Unknown provider: chart.jsProvider <- chart.js <- WhateverCtrl
http://errors.angularjs.org/1.3.20/$injector/unpr?p0=chart.jsProvider%20%3C-hart.js%20%3C-%20WhateverCtrl
at http://mega.app/scripts/vendor.js:9895:12
at http://mega.app/scripts/vendor.js:13863:19
at Object.getService [as get] (http://mega.app/scripts/vendor.js:14010:39)
at http://mega.app/scripts/vendor.js:13868:45
at getService (http://mega.app/scripts/vendor.js:14010:39)
at invoke (http://mega.app/scripts/vendor.js:14042:13)
at Object.instantiate (http://mega.app/scripts/vendor.js:14059:27)
at http://mega.app/scripts/vendor.js:18356:28
at http://mega.app/scripts/vendor.js:44696:28
at invokeLinkFn (http://mega.app/scripts/vendor.js:18113:9)
when I inject chart.js globally like below, I don't get any error. (keep reading)
angular.module('my-app', [
'chart.js' // <<<<<
'ui.router',
'ngStorage',
// ...
]);
})();
But from my understanding, it's recommended to inject this module only in the controllers that uses it, thus when I try to inject it in a controller like below, I get the error above.
angular
.module('my-app')
.controller('WhateverCtrl', ctrl);
ctrl.$inject = ['chart.js']; // <<<<<
function ctrl() {
var vm = this;
// ...
However if I remove the $ from the injection line to ctrl.inject = ['chart.js']; I do get rid of the error, but chart.js wont work, because I guess I must pass it to the function function ctrl() { like this function ctrl(chart.js) { which of course causes an error due to the ..
Since angular-chart.js is itself a module, it must be injected into the module and cannot be injected into the controller.
With JGOakley's clarification, I was able to discover this line in angular-chart.js
return angular.module('chart.js', [])
.provider('ChartJs', ChartJsProvider)
To include this for use in your controller:
YourModule.$inject = ['ChartJs'];
This was a frustrating find, since I took this line to mean I could reference it as chart
define(['angular', 'chart'], factory);
I am trying to set up unit testing with Angular and have hit a bit of a wall with injecting into the module level config and run methods.
For example, if I have a module definition as such:
angular.module('foo', ['ngRoute', 'angular-loading-bar', 'ui.bootstrap']).config(function ($routeProvider, $locationProvider, datepickerConfig, datepickerPopupConfig) {
Karma yells at me because I am not properly mocking $routeProvider, datepickerConfig, etc with the following:
Error: [$injector:modulerr] Failed to instantiate module foo due to:
Error: [$injector:unpr] Unknown provider: $routeProvider
(and then if I remove $routeProvider then it says Unknown provider: datepickerConfig and so on)
I also have the following code in a beforeEach:
angular.mock.module('foo');
angular.mock.module('ngRoute');
angular.mock.module('ui.bootstrap');
And the following in my karma.conf.js:
'components/angular/angular.js',
'components/angular/angular-mocks.js',
'components/angular/angular-route.js',
'components/angular-ui/ui-bootstrap-tpls.js',
'app/*.js', // app code
'app/**/*.js',
'app/**/**/*.js',
'test/app/*.js', // app.js
'test/specs/*.js', // angular.mock.module calls
'test/**/*.js', // tests
'test/**/**/*.js'
Thank you for any advice.
Make sure to include the angular-route module and all your dependencies into the flies array of your karma.conf.js. That should do the trick.
I also have the following code in a beforeEach:
angular.mock.module('foo');
angular.mock.module('ngRoute');
angular.mock.module('ui.bootstrap');
I don't think you need to mock ngRoute and ui.bootstrap
Generally I just set
describe('myApp', function() {
beforeEach(module('foo'));
it('should do something awesome', function() {
// arrange
// act
// assert
});
});
I have the following:
var app = angular
.module('app', ['ui.router', 'admin', 'home', 'questions', 'ngResource', 'LocalStorageModule'])
.config(['$sceProvider', '$stateProvider', '$locationProvider', 'localStorageService',
function ($sceProvider, $stateProvider, $locationProvider, localStorageService ) {
$sceProvider.enabled(false);
$locationProvider.html5Mode(true);
localStorageService.add('adminContent', 'xx');
This is giving me an error:
Uncaught Error: [$injector:modulerr] Failed to instantiate module app due to:
Error: [$injector:unpr] Unknown provider: localStorageService
http://errors.angularjs.org/1.2.0-rc.2/$injector/unpr?p0=localStorageService
at hasOwnPropertyFn (http://127.0.0.1:81/Scripts/angular-v1.2.0-rc.2.js:78:12)
at http://127.0.0.1:81/Scripts/angular-v1.2.0-rc.2.js:2997:19
at getService (http://127.0.0.1:81/Scripts/angular-v1.2.0-rc.2.js:3119:39)
at Object.invoke (http://127.0.0.1:81/Scripts/angular-v1.2.0-rc.2.js:3140:13)
at http://127.0.0.1:81/Scripts/angular-v1.2.0-rc.2.js:3078:37
at Array.forEach (native)
at forEach (http://127.0.0.1:81/Scripts/angular-v1.2.0-rc.2.js:224:11)
at loadModules (http://127.0.0.1:81/Scripts/angular-v1.2.0-rc.2.js:3065:5)
at createInjector (http://127.0.0.1:81/Scripts/angular-v1.2.0-rc.2.js:3007:11)
at doBootstrap (http://127.0.0.1:81/Scripts/angular-v1.2.0-rc.2.js:1152:20)
http://errors.angularjs.org/1.2.0-rc.2/$injector/modulerr?p0=app&p1=Error%3…http%3A%2F%2F127.0.0.1%3A81%2FScripts%2Fangular-v1.2.0-rc.2.js%3A1152%3A20)
Is it not possible to use localStorage in config like this? I have included the code for the localstorage module and it's loaded before the app.js. It works in other parts of the application, but the same line does not work when placed in the app.config
Only providers and constants are injectable in configuration blocks. See the AngularJs documentation for more insights: http://docs.angularjs.org/guide/module (sektion "Module Loading & Dependencies").
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.
While the accepted answer correctly answers the question, it does not offer any solution (for Angular newbies looking for the right way to do this).
You have access to services at run time, which happens after configuration. For instance:
angular.module('app').run(storeAdminContent);
storeAdminContent.$inject = ['localStorageService'];
function storeAdminContent(localStorageService) {
localStorageService.add('adminContent', 'xx');
}
I'm trying to apply routing to my Typescript-based Angular application. The app should get $routeProvider injected with a code like this:
var app = angular.module("MyApp", ["ui.bootstrap"]);
// app.service's and controller's here...
app.config(["$routeProvider",
function ($routeProvider: ng.IRouteProvider) {
$routeProvider
.when("/", {
controller: MyApp.Controllers.ItemsController,
templateUrl: "/Items.html"
})
// ... other routes ...
.otherwise({
redirectTo: "/"
});
}]);
Anyway, when I start the application I get an exception from angular telling me that it cannot find the provider named $routeProviderProvider:
Error: Unknown provider: $routeProviderProvider <- $routeProvider at Error (<anonymous>)
at http://.../Scripts/angular.js:2734:15
at Object.getService [as get] (http://.../Scripts/angular.js:2862:39)
at http://.../Scripts/angular.js:2739:45
at getService (http://.../Scripts/angular.js:2862:39)
at invoke (http://.../Scripts/angular.js:2880:13)
at Object.instantiate (http://.../Scripts/angular.js:2914:23)
at $get (http://.../Scripts/angular.js:4805:24)
at $get.i (http://.../Scripts/angular.js:4384:17)
at forEach (http://.../Scripts/angular.js:137:20) undefined angular.js:5754
By peeking at the angular source (1.0.7), I can tell this comes from the fact that at line 2737 where the instanceInjector is created, its name comes from appending a variable named providerSuffix, whose value is "Provider", to the requested provider name (here "$routeProvider"). Thus, this results in an exception. Yet, the correct name should right be "$routeProvider"; if I change it into just "$route" in my code, this error disappears as expected, as now the built name is "$routeProvider"; but I get another exception telling me that the service "$route" is not defined. So, what should I do to resolve this?
Updated answer:
Like I mentioned in the comments below
e.g. it would be invalid in a controller. FYI The section that you are talking about "ProviderProvider" is just for logging, not how it is internally searching for the dependency
And found in your code:
export class MainController {
static $inject = ["$scope", "$routeProvider"];
constructor(private $scope: IMainScope,
private $routeProvider: ng.IRouteProvider) {
}
}
you cannot have routeProvider in your controllers. Your app.config is not what is giving you the error. Change your controller to and the error goes away:
export class MainController {
static $inject = ["$scope"];
constructor(private $scope: IMainScope) {
}
}
Additionally I recommend not implementing your own scope when using controllers. Here's why: http://www.youtube.com/watch?v=WdtVn_8K17E&feature=youtu.be&t=5m36s
Hope you enjoy angular + typescript as much as I do :)