I've made a handful of Angular applications in the past, but have never really made use of modules. I'm creating an Ionic app right now (which uses Angular) and for some reason I cannot get my services to inject into the controllers.
controllers.js
angular.module('myapp.controllers', [])
.controller('StreamCtrl', ['$scope', 'StreamService',
function($scope, StreamService) {
$scope.playlists = [1,2,3,4];
}]);
services.js
angular.module('myapp.services', [])
.factory('StreamService', ['$scope', function($scope) {
var self = {
'fetching' : false,
'streamItems' : []
};
return self;
]);
app.js
angular.module('myapp', ['ionic',
'myapp.services',
'myapp.controllers',
'ngCordova',
'LocalStorageModule'])
// And lots of other code that isn't needed for this question.
So when I try and load the StreamCtrl I end up getting the following error:
Error: [$injector:unpr] Unknown provider: $scopeProvider <- $scope <-StreamService
http://errors.angularjs.org/1.3.6/$injector/unpr?p0=<ion-nav-view name="menuContent" class="view-container" nav-view-transition="ios">copeProvider%20%3C-%20%24scope%20%3C-%20StreamService
at REGEX_STRING_REGEXP (http://localhost:8100/lib/ionic/js/ionic.bundle.js:7888:12)
at http://localhost:8100/lib/ionic/js/ionic.bundle.js:11806:19
at Object.getService [as get] (http://localhost:8100/lib/ionic/js/ionic.bundle.js:11953:39)
at http://localhost:8100/lib/ionic/js/ionic.bundle.js:11811:45
at getService (http://localhost:8100/lib/ionic/js/ionic.bundle.js:11953:39)
at Object.invoke (http://localhost:8100/lib/ionic/js/ionic.bundle.js:11985:13)
at Object.enforcedReturnValue [as $get] (http://localhost:8100/lib/ionic/js/ionic.bundle.js:11847:37)
at Object.invoke (http://localhost:8100/lib/ionic/js/ionic.bundle.js:11994:17)
at http://localhost:8100/lib/ionic/js/ionic.bundle.js:11812:37
at getService (http://localhost:8100/lib/ionic/js/ionic.bundle.js:11953:39)
See this working fiddle ,
You are treating service as if it was a controller, never inject a $scope into service, its not there
var myApp = angular.module('myApp',['myapp.services','myapp.controllers']);
angular.module('myapp.controllers', [])
.controller('StreamCtrl', ['$scope', 'StreamService',
function($scope, StreamService) {
$scope.playlists = [1,2,3,4];
}]);
angular.module('myapp.services', []).factory('StreamService', function() {
var self = {
'fetching' : false,
'streamItems' : []
};
return self;}
);
Your error clearly says there is problem inside StreamService factory.
You could never inject $scope inside factory. Removing $scope dependency will solve your issue.
Factory basically used to expose singleton methods, common and sharable data. Usually we write any service call or method which can be accessible by any module who inject it using its dependancy.
Factory
angular.module('myapp.services', [])
.factory('StreamService', [function() {
var self = {
'fetching': false,
'streamItems': []
};
return self;
}]);
Working fiddle.
Hope this could help you. Thanks.
Related
I'm having a problem trying to inject $ http to the factory. I miss the following error in Angular 1.6:
Circular dependency found: $rootScope <- $http <- $exceptionHandler <- $rootScope <- $route
And this was what I was doing so far:
var app = angular.module("app", []);
app
.controller("ctrl", function($scope) {
// controller
})
.factory('$exceptionHandler', ['$log', '$http', function($log, $http) {
return function myExceptionHandler(exception, cause) {
$log.warn(exception, cause);
// I show only this and it already throws error
console.log($http);
}
}]);
To work around the circular dependency, try the following.
Instead of injecting $http directly into the interceptor try injecting the $injector and use that directly to get $http.
var app = angular.module("app", []);
app.factory('$exceptionHandler', ['$log', '$injector', function($log, $injector) {
return function myExceptionHandler(exception, cause) {
var $http = $injector.get('$http');
$log.warn(exception, cause);
// I show only this and it already throws error
console.log($http);
}
}]);
I'm trying to ensure that upon loading the frame state, that my $rootScope has all the necessary properties defined from previous states.
The ionic.utils module is properly injected into my angular app. This module comes from my services.js file.
angular.module('ionic.utils', [])
.factory('dataService', ['$rootScope','$q','$timeout', function($rootScope, $q, $timeout) {
return {
get: function() {
var deferred = $q.defer();
$timeout(function() {
deferred.resolve($rootScope);
}, 2000);
return deferred.promise;
}
}
}]);
Inside my controllers.js file, this is the corresponding controller for my frame state:
.controller('FrameCtrl', ['$scope','$state','$rootScope','dataService',
function($scope, $state, $rootScope, dataService) {
// get active address and delivery time.
dataService.get().success(function() {
console.log("derp");
});
}])
However, this controller returns the following console error upon state transition:
ionic.bundle.js:17696 TypeError: Cannot read property 'get' of undefined
at new <anonymous> (controllers.js:201)
at invoke (ionic.bundle.js:11591)
at Object.instantiate (ionic.bundle.js:11602)
at $get (ionic.bundle.js:14906)
at updateView (ionic.bundle.js:42986)
at IonicModule.directive.directive.compile.eventHook (ionic.bundle.js:42933)
at Scope.$get.Scope.$broadcast (ionic.bundle.js:20605)
at $state.transitionTo.$state.transition.resolved.then.$state.transition (ionic.bundle.js:34122)
at deferred.promise.then.wrappedCallback (ionic.bundle.js:19197)
at ionic.bundle.js:19283
I'm having trouble finding the error in the service I've written. Some help would be greatly appreciated!
EDIT
After adding the dependency injections into my controller, now the error has changed. Here it is:
TypeError: object is not a function
at new <anonymous> (controllers.js:202)
at invoke (ionic.bundle.js:11591)
at Object.instantiate (ionic.bundle.js:11602)
at $get (ionic.bundle.js:14906)
at updateView (ionic.bundle.js:42986)
at IonicModule.directive.directive.compile.eventHook (ionic.bundle.js:42933)
at Scope.$get.Scope.$broadcast (ionic.bundle.js:20605)
at $state.transitionTo.$state.transition.resolved.then.$state.transition (ionic.bundle.js:34122)
at deferred.promise.then.wrappedCallback (ionic.bundle.js:19197)
at ionic.bundle.js:19283
Your dependency array in controller is missing numerous dependencies passed to arguments
.controller('FrameCtrl', [ 'Rootscope', function($scope, $state,
$rootScope, Rootscope) {
Should be
.controller('FrameCtrl', ['$scope','$state', '$rootScope', 'Rootscope', function($scope, $state,
$rootScope, Rootscope) {
Sure seems confusing to me to name a service Rootscope!
Normally with promises we just use .then, which takes the success function as the first parameter and the error function as the second.
success and error are functions on a promise that AngularJS adds
for us when using $http or $resource. They're not standard, you
won't find them on other promises.
Code
dataService.get().then(function() {
console.log("derp");
});
Return was missing from deferred.resolve()
angular.module('ionic.utils', []).factory('dataService', ['$rootScope', '$q', '$timeout', function($rootScope, $q, $timeout) {
return {
get: function() {
var deferred = $q.defer();
$timeout(function() {
return deferred.resolve($rootScope);
}, 2000);
return deferred.promise;
}
}
}]);
Hopefully this will help you. Thanks.
I have the strange issue, that somehow my own provider is not injecting correctly into my app.
This is my provider:
angular.module '1425App'
.provider 'OData',[() ->
#_baseUrl = ''
return {
setBaseUrl: (value) ->
#_baseUrl = value
return
$get: ['$http', '$q', ($http, $q) ->
return {
getAll: (resource) ->
dfd = $q.defer()
$http.get("#{#_baseUrl}/#{resource}").success (res) ->
console.log res
dfd.resolve()
return
return dfd.promise
}
]
}
]
This is my app + config block:
angular.module('1425App', [
'ngCookies',
'ngResource',
'ngSanitize',
'ui.router',
'angular-loading-bar',
'ngAnimate',
'toaster',
'ui.gravatar',
'ngFitText',
'google-maps',
'mm.foundation',
'restangular',
'ui.select2',
'ngTable',
'ngGrid',
'ngCsv',
'ui.date',
'ngDragDrop',
'ui.sortable'
])
.config ($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider, cfpLoadingBarProvider, baseUrl, ODataProvider) ->
$httpProvider.interceptors.push('httpInterceptor')
ODataProvider.setBaseUrl(baseUrl + '/odata/')
cfpLoadingBarProvider.includeSpinner = false
...
Im getting following error:
Uncaught Error: [$injector:modulerr] Failed to instantiate module
1425App due to: Error: [$injector:unpr] Unknown provider:
ODataProvider
This leads to my believe, that its an issue with injecting the provider into my app. Any idea what im missing?
Looking at you pasted snippet issue could be that you have config block appearing before oData provider has been registered. Try setting up the config block after the oDataProvider registration.
Separate out config block from app registration and load it after your provider(s) have been registered. You can only configure the providers that are registered before the specific config block that uses it. This is not the case with constant though you can have them registered in any order.
The above information (which was a bug) is as of 1.2.* version of angular, with 1.3 you can register providers even after the config block.
I've looked at several answers on SO, and have read through the official docs. From what I've seen, this should be working.
In my service, I have:
var RESTServices = angular.module('RESTServices', []);
RESTServices.service('restCall', ['$resource', function($resource){
return {
servList: function(args, scope){
// Lists servers from the API method
$resource('http://localhost:1000/apiv1/servers')
.get(args).$promise.then(function (result) {
scope.servers = result.server_list;
}, function(error){
errorHandler(scope, error)
})
}, ... etc.,
};
}]);
I am trying to run the test:
describe('Services that require API calls', function() {
var restCall,
$httpBackend;
beforeEach(function () {
module('RESTServices');
inject(function (_$httpBackend_, _restCall_) {
restCall = _restCall_;
$httpBackend = _$httpBackend_;
});
});
it('Should supply a list of servers', function () {
//var serverList = ['Thufir Hawat', 'Duncan Idaho'];
//$httpBackend.expectGET('http://localhost:1000/apiv1/servers')
// .respond({server_list: serverList});
// Would continue writing if $resource could inject...
});
});
However I receive the following message when I do:
Chrome 31.0.1650 (Windows Vista) Services that require API calls Should supply a list of servers FAILED
Error: [$injector:unpr] Unknown provider: $resourceProvider <- $resource <- restCall
http://errors.angularjs.org/1.2.11/$injector/unpr?p0=%24resourceProvider%20%3C-%20%24resource%20%3C-%20restCall
at E:/workspace/JSUI/app/lib/angular/angular.js:78:12
at E:/workspace/JSUI/app/lib/angular/angular.js:3543:19
at Object.getService [as get] (E:/workspace/JSUI/app/lib/angular/angular.js:3670:39)
at E:/workspace/JSUI/app/lib/angular/angular.js:3548:45
at getService (E:/workspace/JSUI/app/lib/angular/angular.js:3670:39)
at invoke (E:/workspace/JSUI/app/lib/angular/angular.js:3697:13)
at Object.instantiate (E:/workspace/JSUI/app/lib/angular/angular.js:3718:23)
at Object.<anonymous> (E:/workspace/JSUI/app/lib/angular/angular.js:3586:24)
at Object.invoke (E:/workspace/JSUI/app/lib/angular/angular.js:3707:17)
at E:/workspace/JSUI/app/lib/angular/angular.js:3549:37
Error: Declaration Location
at window.inject.angular.mock.inject (E:/workspace/JSUI/app/lib/angular/angular-mocks.js:2134:25)
at null.<anonymous> (E:/workspace/JSUI/test/unit/servicesSpec.js:133:9)
Chrome 31.0.1650 (Windows Vista): Executed 30 of 30 (1 FAILED) (0.497 secs / 0.14 secs)
Everything I've read tells me this should be working, so what am I missing? It IS possible to use $httpBackend to mock $resource, correct? Posts from the google group (including this one seem to suggest that it should work without a problem.
After trying many things, and doing some more research, it seems to be that the module that includes the declaration of 'ngResource' needs to be included in the test for it to work. For example, I declared 'ngResource' in app.js in the module myApp, so once I include this, the tests work.
in app.js:
var myApp = angular.module('myApp', [
'ngRoute',
'ngCookies',
'ngResource',
'myControllers',
'myDirectives',
'myServices',
'RESTServices'
]);
in the spec:
describe('Testing Services that require API calls', function() {
var restCall,
$httpBackend,
$resource,
scope;
beforeEach(function () {
module('myApp'); //this line fixed it
module('RESTServices');
inject(function (_$httpBackend_, _restCall_, _$resource_) {
$httpBackend = _$httpBackend_;
$resource = _$resource_;
restCall = _restCall_;
});
scope = {};
});
describe('servList', function() {
it('Should supply a list of servers', function () {
var serverList = ['Thufir Hawat', 'Duncan Idaho'];
$httpBackend.expectGET('http://localhost:1000/apiv1/servers')
.respond({server_list: serverList});
restCall.servList({}, scope);
$httpBackend.flush();
expect(arraysEqual(scope.servers, serverList)).toBeTruthy();
});
});
});
ETA: Someone else chimed in that they fixed this problem for themselves by adding module('ngResource'); where I added module('RESTServices');. I'm willing to bet that the important thing is having a ngResource imported into your tests, either directly or indirectly.
I have a problem with providers in angular, I get this error:
Row 2696: Error: Unknown provider: a
Using unminified angular v 1.06, Row 2696:
providerInjector = createInternalInjector(providerCache, function() {
throw Error("Unknown provider: " + path.join(' <- '));
}),
This is the code:
var myApp = angular.module('myApp', [], function ($interpolateProvider) {
$interpolateProvider.startSymbol('{[{');
$interpolateProvider.endSymbol('}]}');
});
myApp.directive('buttonsRadio', function() {
[...]
});
myApp.controller('MainController', function MainController ($scope) {
[...]
})
Any ideas?
Edit: Added error message:
Error: Unknown provider: aProvider <- a
createInjector/providerInjector<#/libs/angular.js:2696
getService#/libs/angular.js:2824
createInjector/instanceCache.$injector<#/libs/angular.js:2701
getService#/libs/angular.js:2824
invoke#/libs/angular.js:2842
instantiate#/libs/angular.js:2874
#/libs/angular.js:4759
applyDirectivesToNode/nodeLinkFn/<#/libs/angular.js:4338
forEach#/libs/angular.js:138
nodeLinkFn#/libs/angular.js:4323
compositeLinkFn#/libs/angular.js:3969
compositeLinkFn#/libs/angular.js:3972
nodeLinkFn#/libs/angular.js:4354
compositeLinkFn#/libs/angular.js:3969
publicLinkFn#/libs/angular.js:3874
bootstrap/resumeBootstrapInternal/</<#/libs/angular.js:963
Scope.prototype.$eval#/libs/angular.js:8011
Scope.prototype.$apply#/libs/angular.js:8091
bootstrap/resumeBootstrapInternal/<#/libs/angular.js:961
invoke#/libs/angular.js:2857
bootstrap/resumeBootstrapInternal#/libs/angular.js:960
bootstrap#/libs/angular.js:973
angularInit#/libs/angular.js:934
#/libs/angular.js:14756
f.Callbacks/n#http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js:2
f.Callbacks/o.fireWith#http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js:2
.ready#http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js:2
B#http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js:2
/libs/angular.js
Line 5704
Before running uglify or any other ofuscator/compression algorithm, you should run the grunt task ng-annotate which adds the dependencies as strings for you as an injectable array, this makes your code a lot cleaner as the injectable annotaion can be added automatically as a build step rather than having to manually code it.
myApp.controller('MainController', function MainController ($scope) {
[...]
})
Becomes:
myApp.controller('MainController', ['$scope', function MainController ($scope) {
[...]
}])
Take a look at: https://github.com/olov/ng-annotate
Update:
ng-min AngularJS Pre-minifier deprecated –> use ng-annotate
I assume you're using some kind of javascript obufuscator (Clousure, SquishIt, UglifyJS etc).
In this case you need to specify dependencies in such way:
var myApp = angular.module('myApp', [], ['$interpolateProvider',function ($interpolateProvider) {
$interpolateProvider.startSymbol('{[{');
$interpolateProvider.endSymbol('}]}');
}]);
myApp.directive('buttonsRadio', function() {
[...]
});
myApp.controller('MainController',['$scope', function MainController ($scope) {
[...]
}])
Note to specify dependenices for dependency injection - instead of passing function, you need to specify array with list of strings with names of objects to inject into parameters and the function itself.