I'm creating an app using angularjs, I have two controllers, and one service (all in separated files). My problem is that I can't inject the service in the controllers. I used the service in the same file as the controller and it worked, but I want it in separated files to share data between controllers.
This is one of my controllers
var app = angular.module('cloudpear', ['ngRoute','ServiceUser']);
app.controller('login',['$scope','$location','$window', function(ServiceUser,$scope,$location,$window){
.....
}]);
This is my service
var app = angular.module('service', []);
app.factory('ServiceUser',function(){
var user=[];
return{
add: add,
get: get
}
function add(item){
user.push(item);
}
function get(){
return user;
}
return user;
});
I get this error:
Uncaught Error: [$injector:modulerr] http://errors.angularjs.org/1.6.4-build.5327+sha.233f47b/$injector/modulerr…20(https%3A%2F%2Fcode.angularjs.org%2Fsnapshot%2Fangular.min.js%3A22%3A179)
Can you tell me what I'm doing wrong, I've searched everywhere and tried multiple solution, but nothing worked!
As you have define a separate module for service, you need to inject service module in the cloudpear module declaration.
var app = angular.module('cloudpear', ['ngRoute','service']);
//^^^^^^^^^^ instead of ServiceUser
Then need to inject service in controller
app.controller('login',['ServiceUser', '$scope','$location','$window',function(ServiceUser, $scope,$location,$window){
Related
I want to share a specific property across different components' (different controllers in them).
Trying to inject a service that I created in the app module.
The service has get/set functions.
I created the service called SharedProperties. It's not accessible from any component in my app, says "Uknown". Why?
This is how my app is defined, and here's the very simple service.
mapotApp = angular
.module('mapotApp', [])
.service('sharedProperties', function () {
var property = 'test';
return {
getProperty: function () {
return property;
},
setProperty: function(value) {
property = value;
}
};
});
and then in my component:
angular.module('aboutPage').component('aboutPage', {
templateUrl: 'app/about-page/about-page.html',
controller: ['sharedProperties', function AboutPageController($http, $scope, sharedProperties) {
var self= this;
//inherting global variable
self.prop = sharedProperties.getProperty(); //UNKOWN PROVIDER ERROR HERE
}]
});
This returns:
Error: [$injector:unpr] Unknown provider: sharedPropertiesProvider <- sharedProperties
What can I do?
I tried injecting it in tons of places and still doesn't work.
thanks so much.
Here is the working DEMO with your code.
Your shareable service is correct, but make sure to properly initialize your controller, as well as correctly inject $http and $scope in it (see how $scope and $timeout services are injected in my demo).
I have an existing application that uses a MapProvider like so:
mapModule.factory('MapProvider', ['$injector', function($injector) {
return $injector.get('GoogleMapsService');
}]);
This MapProvider is used extensively across the application and is injected into various other controllers and services (rightly or wrongly).
I now need to add a BaiduMapsService, which I have been able to get working as a test with:
mapModule.factory('MapProvider', ['$injector', function($injector) {
if(true) {
return $injector.get('GoogleMapsService');
} else {
return $injector.get('BaiduMapsService');
}
}]);
And flipping the if value accordingly. (Both of these services are using a TypeScript interface, so have the same methods). Now, I need to add a $http call to the API, which will return which map to use, based on the provided data. How can I make my factory asynchronous, without having to change all my MapProvider.someCallHere() calls to MapProvider.then(m => m.someCallHere()).
Ideally, when MapProvider is injected across my application, it will be able to resolve using the async data (only once), and then inject the necessary service afterwards.
Alternatively, is there a way to defer / delay loading Angular at all, until I make an API call and set some global data somewhere?
Thanks.
You can postpone the application bootstrap (also, don't use ng-app, do it manually) until you get data from server. I've answered this before on this question but each case has its own specific details.
I usually see a config value being declared on the app before the application gets bootstraped, this is very useful for multi-tenant apps. So that this preference values can be used in the whole app as an injected provider.
For example:
var app = angular.module('app', []);
// retrieve the $http provider
var ngInjector = angular.injector(["ng"]);
var $http = ngInjector.get("$http");
// load config function. Returns a promise.
function loadConfig(){
return $http.get("/config.json").then(function(response) {
// declare the configuration value on your app
app.constant("Config", response.data);
}, function(err) {
console.error("Error loading the application config.", err)
});
}
// Call loadConfig then bootstrap the app
loadConfig().then(function () {
angular.element(document).ready(function() {
angular.bootstrap(document, ["app"]);
});
});
Finally from your factory, you can use the Config constant to retrieve the preferred map.
mapModule.factory('MapProvider', ['$injector', 'Config', function($injector, Config) {
if(Config.preferedMap == 'GoogleMap') {
return $injector.get('GoogleMapsService');
} else {
return $injector.get('BaiduMapsService');
}
}]);
Only way I can think is to hold initialize whole angular (and modules) until you got your "config" (and set is as global variable).
app.js
angular
.module('yelp',['ngRoute']) //creating module
.config(config);
config.$inject=['$routeProvider','YelpService']; //YelpService is unknown here
function config($routeProvider){
$routeProvider
.when('/restaurants',{
templateUrl:'partials/restaurants.html',
controller:'RestaurantsController',
controllerAs:'rstrntsCtrl',
resolve:{
getRestaurants:getRestaurants
}
});
}
function getRestaurants(YelpService){
// i need route params here
}
YelpService.js
angular
.module('yelp') //using already created module
.factory('YelpService',YelpService);
YelpService.$inject=['$http'];
function YelpService($http){
var factory={
}
return factory;
}
I loaded app.js file first and then yelpService.js file. Since service is loaded later i think it is not available to inject. If i load service file first, it causes an error, since the module in created in config file. How to overcome this. Moreover in config file how to get routeparams in getRestaurants method
For yelp use the provider call YelpServiceProvider.
For the resolve just wrap in a function and inject the desired functionality.
angular
.module('yelp',['ngRoute']) //creating module
.config(config);
config.$inject=['$routeProvider','YelpServiceProvider']; //YelpService is unknown here
function config($routeProvider){
$routeProvider
.when('/restaurants',{
templateUrl:'partials/restaurants.html',
controller:'RestaurantsController',
controllerAs:'rstrntsCtrl',
resolve:{
restaurants:function($routeParams, YelpService) {
return getRestaurants($routeParams, YelpService);
}
}
});
}
function getRestaurants($routeParams, YelpService){
// i need route params here
}
Resolve is used to, well, resolve your data. So the left hand assignment would not be a function name as you have perhaps meant but the variable list of items (a promise actually).
Then in your controller after having injected 'restaurants':
this.restaurants; // or $scope.restaurants if you are not using controllerAs
Having a little trouble doing this, I know you need to do it with $get, however the examples I find don't quite fall into the structure I need.
My provider looks like so
.provider('moduleOb', function(){
var modules = {};
//outer closure to inject "this"
function outerAddState(moduleObj) {
};
//outer function so we can push into module
function modulePush(currentModule, name){
}
return {
goState: function(name, stateName, options) {
//I need to use this function in here which injected from a factory
checkUrl.requestUrl();
},
moduleConstructor: function(name, cb) {
},
$get: function $get() {
return this;
}
}
})
So I'm just planting the get right now at the bottom as you can see. I need to be able to inject the checkurl factory somehow so I have access to it in goState. How would I format it so I can use the get to do so?
So if I separate the factory out into a separate module I can inject it with
var injector = angular.injector(['urlParsing']);
var checkUrl = injector.get('checkUrl')
inside the provider. However, the checkurl function has a dependency on $location. So the injector cannot find $location now --
Error: [$injector:unpr] Unknown provider: $locationProvider <- $location <- checkUrl
Can I inject the location provider too? Is this the best way of doing this?
Maybe with a get ($location)? Or maybe there is a better work around. Could use some help, this has been holding me up all day! Thanks for reading.
Edit2: After a few days of wrestling with this - I realize I'm just looking how to inject multiple modules ( a factory and it's dependancies of $log and $location) to a provider. The real reason being so I can let people set what I want them to set in the .config.
I have temporarily circumvented this by changing this provider to a factory and using the .run instead of the .config BUT I would still love to be able to use .config instead.
The main question - is it possible? I tried with no luck..
main app.js
...
var app = angular.module('myApp', ['services']);
app.config(['customProvider', function (customProvider) {
}]);
...
provider itself
var services = angular.module('services', []);
services.provider('custom', function ($http) {
});
And I've got such error:
Uncaught Error: Unknown provider: $http from services
Any ideas?
Thanks!
The bottom line is:
You CANNOT inject a service into the provider configuration section.
You CAN inject a service into the section which initializes the provider's service.
Details:
Angular framework has a 2 phase initialization process:
PHASE 1: Config
During the config phase all of the providers are initialized and all of the config sections are executed. The config sections may contain code which configures the provider objects and therefore they can be injected with provider objects.
However, since the providers are the factories for the service objects and at this stage the providers are not fully initialized/configured -> you cannot ask the provider to create a service for you at this stage -> at the configuration stage you cannot use/inject services.
When this phase is completed all of the providers are ready (no more provider configuration can be done after the configuration phase is completed).
PHASE 2: Run
During run phase all the run sections are executed. At this stage the providers are ready and can create services -> during run phase you can use/inject services.
Examples:
1. Injecting the $http service to the provider initialization function WILL NOT work
//ERRONEOUS
angular.module('myModule').provider('myProvider', function($http) {
// SECTION 1: code to initialize/configure the PROVIDER goes here (executed during `config` phase)
...
this.$get = function() {
// code to initialize/configure the SERVICE goes here (executed during `run` stage)
return myService;
};
});
Since we are trying to inject the $http service into a function which is executed during the config phase we will get an error:
Uncaught Error: Unknown provider: $http from services
What this error is actually saying is that the $httpProvider which is used to create the $http service is not ready yet (since we are still in the config phase).
2. Injecting the $http service to the service initialization function WILL work:
//OK
angular.module('myModule').provider('myProvider', function() {
// SECTION 1: code to initialize/configure the PROVIDER goes here (executed during `config` phase)
...
this.$get = function($http) {
// code to initialize/configure the SERVICE goes here (executed during `run` stage)
return myService;
};
});
Since we are now injecting the service into the service initialization function, which is executed during run phase this code will work.
This might give you a little leverage:
var initInjector = angular.injector(['ng']);
var $http = initInjector.get('$http');
But be careful, the success/error callbacks might keep you in a race-condition between the app start and the server response.
This is an old question, seems we have some chicken egg thing going on if we want to rely on the core capability of the library.
Instead of solving the problem in a fundamental way, what I did is by-pass. Create a directive that wraps the whole body. Ex.
<body ng-app="app">
<div mc-body>
Hello World
</div>
</body>
Now mc-body needs to be initialized before rendering (once), ex.
link: function(scope, element, attrs) {
Auth.login().then() ...
}
Auth is a service or provider, ex.
.provider('Auth', function() {
... keep your auth configurations
return {
$get: function($http) {
return {
login: function() {
... do something about the http
}
}
}
}
})
Seems to me that I do have control on the order of the bootstrap, it is after the regular bootstrap resolves all provider configuration and then try to initialize mc-body directive.
And this directive seems to me can be ahead of routing, because routing is also injected via a directive ex. <ui-route />. But I can be wrong on this. Needs some more investigation.
In response to your question, "Any Ideas?", I would have respond with "yes". But wait, there's more!
I suggest just using JQuery in the config. For example:
var app = angular.module('myApp', ['services']);
app.config(['$anyProvider', function ($anyProvider) {
$.ajax({
url: 'www.something.com/api/lolol',
success: function (result) {
$anyProvider.doSomething(result);
}
});
}]);