Injecting Dependencies in config() modules - AngularJS - javascript

Currently in app.js i have the following routes:
var gm = angular.module('gm', ['gm.services','gm.directives','gm.filters','gm.controllers','ngSanitize']);
gm.config(['$routeProvider', 'Path', function($routeProvider, Path) {
$routeProvider.when('/login', {
templateUrl: Path.view('application/authentication/login.html'),
controller: 'authController'
});
$routeProvider.when('/dashboard', {
templateUrl: Path.view('application/dashboard/index.html'),
controller: 'dashboardController'
});
$routeProvider.otherwise({
redirectTo: '/login'
});
}]);
I'm trying to inject the Path dependency as you can see. Although i get an error saying it can't find this provider. I think this is because config module providers are executed first before anything else. below is my Path provider definition in "services.js"
gm.factory("Path", function() {
return {
view: function(path) {
return 'app/views/' + path;
},
css: function(path) {
return 'app/views/' + path;
},
font: function(path) {
return 'app/views/' + path;
},
img: function(path) {
return 'app/views/' + path;
},
js: function(path) {
return 'app/views/' + path;
},
vendor: function(path) {
return 'app/views/' + path;
},
base: function(path) {
return '/' + path;
}
}
});
how can i inject this provider into a config module?

angular.config only accepts Providers
every service, factory etc are instances of Provider
So to inject a service in config you just need to call the Provider of the service by adding 'Provider' to it's name.
angular.module('myApp')
.service('FooService', function(){
//...etc
})
.config(function(FooServiceProvider){
//...etc
});
According to the angularjs Provider documentation
... if you define a Factory recipe, an empty Provider type with the $get method set to your factory function is automatically created under the hood.
So if you have a factory (or service) such as:
.factory('myConfig', function(){
return {
hello: function(msg){
console.log('hello ' + msg)
}
}
})
You first need to invoke your factory using the $get method before accessing the returned object:
.config(function(myConfigProvider){
myConfigProvider
.$get()
.hello('world');
});

In .config you can only use providers (e.g. $routeProvider). in .run you can only use instances of services (e.g. $route). You have a factory, not a provider. See this snippet with the three ways of creating this: Service, Factory and Provider
They also mention this in the angular docs https://docs.angularjs.org/guide/services

You should use constant for that, because it's the only thing you can inject in the config phase other than providers.
angular.module("yourModule").constant("paths", {
base: function(){ ... }
});

This discussion helped me when I was trying to figure out the same thing, basically
$routeProvider.when('/', {
templateUrl:'views/main.html',
controller:'MainController',
resolve: {
recentPosts: ['$q', 'backendService', function($q, backendService){
var deferred = $q.defer();
backendService.getRecentPosts().then(
function(data) {
var result = data.result;
deferred.resolve(result);
},
function(error) {
deferred.reject(error);
}
);
return deferred.promise;
}]
}
})

Related

AngularJS Provider wont inject into angular.config

I'm trying to set up some constants for an Angular config through a provider, but for some reason I can't see, I keep getting the error:
Unknown provider: myprovider
I have plenty of dependency injection throughout my project, but I can't figure out why this one will not work.
The order of the code below is the same order as in my config.js.
Provider
var trybConfig = angular.module('trybConfig', []);
trybConfig.provider('myprovider', function() {
this.Routes = {
EventList: {
Location: "/Event",
Template: "views/eventView.html",
Controller: "eventController"
}
}
this.$get = function () {
return this.Routes;
}
});
Config
trybConfig.config(function($routeProvider, myprovider) {
$routeProvider
.when('/Event', {
templateUrl: 'views/eventView.html',
controller: 'eventController'
})
.otherwise({
redirectTo: '/Event'
});
});
Don't add "provider" to the name of your service prodiver, just do:
trybConfig.provider('my', function() {
And inject it:
trybConfig.config(function($routeProvider, myProvider) {
FYI - In your current state you need to inject:
trybConfig.config(function($routeProvider, myproviderProvider) {

Can't resolve variables from ui-router resolve object into controller

I'm using ui-router in my app.
app.config(['$stateProvider', function($stateProvider){
$stateProvide.state('State1', {
url:'/State1',
resolve: {
data: function(Service){
return Service.init();
}
},
views: {
"header": {
templateUrl: 'views/header.tpl.html'
},
"center":{
templateUrl: 'views/center.tpl.html'
},
"footer": {
templateUrl: 'views/footer.tpl.html'
}
}
}
})
}
]);
I tried to load a json file from the server and resolve it in the router.
In the resolve object, I call to my service that is responsible for returning promise.
Service.js:
app.service("Service", ['$rootscope', '$http', function($rootscope, $http){
var promise;
this.init = function(){
promise = this.loadData();
return promise;
};
this.loadData = function(){
var url = "users/getData/json.json";
return $http.get(url).then(function(response){
return response.data;
}, function(error){
alert(error);
})
};
}])
center.tpl.html:
<aside id="first-item" ng-controller="FirstController as firstController">
<first-directive>
</aside>
<aside id="second-item" ng-controller="SecondController as secondController">
<second-directive>
</aside>
This is the controller to which I would like to get the resolved data.
FirstController.js:
app.controller('FirstController', ['$scope', 'data', function($scope, data){
this.myData = data;
}]);
I got the next error: Unknown provider: dataProvider < - data. Why?
since promise is never resolved before returning. try this one.
app.service("Service", ['$rootscope', '$http', function($rootscope, $http){
var promise;
this.init = function(){
return this.loadData();
};
this.loadData = function(){
var url = "users/getData/json.json";
return $http.get(url).then(function(response){
return response.data;
}, function(error){
alert(error);
})
};
}])
From the ui-router docs:
// The controller waits for every one of the above items to be
// completely resolved before instantiation. For example, the
// controller will not instantiate until promiseObj's promise has
// been resolved. Then those objects are injected into the controller
// and available for use.
It is cleared that the resolved variables will only be available in the controllers defined in the state config. You can not resolve those variables in a normal controller and you are trying to use data from your state config in your controller and hence you are getting that error.
But, to get those data in your FirstController, you can do like this:
app.config('$stateProvider', ['$rootScope', function ($stateProvider) {
$stateProvide.state('State1', {
url: '/State1',
resolve: {
data: function (Service) {
var data = Service.init();
$rootScope.$broadcast("dataReceivedFoo", {data: data});
return data;
}
},
views: {
"header": {
templateUrl: 'views/header.tpl.html'
},
"center": {
templateUrl: 'views/center.tpl.html'
},
"footer": {
templateUrl: 'views/footer.tpl.html'
}
}
})
}]);
And, then read in your controller:
app.controller('FirstController', ['$scope' function($scope){
$scope.$on("dataReceivedFoo", function(response) {
$scope.myData = response.data;
})
}]);
Basically, we are broadcasting the data from your state configuration and then receiving in your FirstController.
I think adding controller: 'FirstController' line to state will solve the problem.
app.config(['$stateProvider', function($stateProvider){
$stateProvide.state('State1', {
url:'/State1',
controller: 'FirstController',
resolve: {
data: function(Service){
return Service.init();
}
},
views: {
"header": {
templateUrl: 'views/header.tpl.html'
},
"center":{
templateUrl: 'views/center.tpl.html'
},
"footer": {
templateUrl: 'views/footer.tpl.html'
}
}
}
})
}
]);
Resolve
You can use resolve to provide your controller with content or data
that is custom to the state. resolve is an optional map of
dependencies which should be injected into the controller.
If any of these dependencies are promises, they will be resolved and
converted to a value before the controller is instantiated and the
$stateChangeSuccess event is fired.
The resolve property is a map object. The map object contains
key/value pairs of:
key – {string}: a name of a dependency to be injected into the
controller.
factory - {string|function}: If string, then it is an
alias for a service. Otherwise if function, then it is injected and
the return value is treated as the dependency. If the result is a
promise, it is resolved before the controller is instantiated and its
value is injected into the controller.
https://github.com/angular-ui/ui-router/wiki

Pass sub-state from sub-module to the main module in angular.js with angular-ui-router

I design my SPA like this:
angular.module('app', ['submodule0', 'submodule1']);
Main module:
$stateProvider.state("sub0index", {
url: "/sub0",
// pass states defined in submodule0, is that possible?
}).state("sub1index", {
url: "/sub1",
// pass states defined in submodule1
})
And here are some states defined in submodule0
$stateProvider.state("index", {
url: "/index",
templateUrl: "template/index.html"
}).state("info", {
url: "/info",
templateUrl: "template/info.html"
})
So is that possible that I pass sub-state from sub-module to the main module? I ask this because now I define all my state in my main module, I think it may be more elegant to define the state of one submodule in the submodule itself.
And another question is: I'm not sure my module design is reasonable or not, is my submodules not necessary? Or just keep my whole app logic to one module? Thanks.
====Edited====
And here is the problem I've met.
var app = angular.module('test', ['ui.router', 'app.sub']);
app.config(['$stateProvider', function ($stateProvider) {
$stateProvider.state('index', {
url: "/a",
views: {
"general": {
templateUrl: "/template.html"
}
},
resolve: {
data: 'GetDataService'
}
});
}
The service GetDataService is defined in my submodule app.sub, and here is the service:
angular.module('app.sub',['ui.router'])
.service('GetDataService', ['$stateParams', function($stateParams) {
console.log($stateParams);
return null; // return null just for demo
}]);
The output of console.log($stateParams) is an empty object. But if use the service which is defined in its own module, the current state can be get correctly. So whats the issue?
===Edit===
Thanks for the example, it works fine if give a factory to data directly. But how about I give it a string?
I check the document of ui-router, and there is something about map object in resolve:
factory - {string|function}: If string then it is alias for service.
So if I use the code like this:
resolve: {
data: "GetDataService"
}
And the definition of GetDataService:
.service('GetDataService', ['$stateParams', function($stateParams) {
console.log($stateParams);
return null;
}])
But output of console.log($stateParams) is always an empty object.
Do I have some misunderstanding about the api document?
===Edit again===
If I use code like this:
resolve: {
// data: "GetDataService"
data: ['$stateParams', function($stateParams) {
console.log($stateParams);
return null;
}]
}
I can get the params object.
I would say, that modules should not stop us... we can split the app into many if needed.
But I would suggest: Services should be independent on $state.current. We should pass to them function parameters as needed, but these should be resolved outside of the Service body.
Bette would be to show it in action - there is one working example
This is the service:
angular.module('app.sub',['ui.router'])
.service('DataService', ['$state', function($state) {
return {
get: function(stateName, params){
console.log(stateName);
console.log(params);
return stateName;
}
}
}]);
And here is some adjsuted state def:
app.config(['$stateProvider', function ($stateProvider) {
$stateProvider.state('index', {
url: "/a/{param1}",
views: {
"general": {
templateUrl: "tpl.html"
}
},
resolve: {
data: ['DataService','$stateParams'
, function(DataService,$stateParams, $state){
return DataService.get('index', $stateParams)
}],
},
});
}])
Hope it helps a bit. The plunker link
Because this approach is ready to test service without any dependency on some "external" $state.current. We can just pass dummy, testing params

$routeProvider and resolve : more complex factory passed as string

Please check this Plunk
What I'm doing is I have following AngularJS module:
var synergy_module = angular.module('synergy', ['ngRoute'])
.config(function($provide, $routeProvider, $httpProvider) {
$routeProvider.when('/', {
template: '<div>{{here}}</div>',
controller: SpecificationCtrl,
resolve: {
w: "myFct"
}
});
$routeProvider.otherwise({
redirectTo: '/'
});
});
synergy_module.factory("myFct", ["$http",
function($http) {
return $http.get("http://api.openweathermap.org/data/2.5/weather?q=London,uk");
}
]).factory("my.Fct", ["$http",
function($http) {
var o = {
getWeather: function() {
return $http.get("http://api.openweathermap.org/data/2.5/weather?q=London,uk");
}
}
return o;
}
])
function SpecificationCtrl($scope, w) {
$scope.here = w.data;
}
This works fine as the myFct factory has a simple name and returns $promise but it is not very useful. My factories look more like my.Fct:
they have dots in names (aka "packages") and
return object with methods (these methods return $promise)
How can I pass this "complex" my.Fct factory to the resolve as a string and specify which method of the factory should be used when resolving?
Update:
OK, based on some more search, this works for me:
resolve: {w: ["my.Fct",function (myFct) {
return myFct.getWeather();
}]}
But it is much more code, using the string seems more readable to me...

Injecting services/constants into provider in Angularjs

The problem here is I am able to access the getRoutes(), but I am unable to access the injected constant -"configuration". What am I missing? Thanks.
(function () {
'use strict';
var app = angular.module('app');
app.constant('configuration', {
PARTIAL_PATH: "/app/components/partials"
});
app.module('app', [
'routeService'
]);
var routeServiceModule = angular.module('routeService', ['common']);
routeServiceModule.provider('routeConfig',function () {
this.getRoutes = function () {
return [
{
url: '/login',
config: {
title: 'admin',
templateUrl: 'app/components/login/login.html'
}
}, {
url: '/',
config: {
templateUrl: 'app/components/dashboard/dashboard.html',
title: 'Dashboard'
}
}
];
};
this.$get = ['configuration', function (configuration) {
var service = {
getRoutes: getRoutes(),
configuration: configuration.PARTIAL_PATH
};
return service;
}];
app.config(['$routeProvider', 'routeConfigProvider', function ($routeProvider, routeConfigProvider) {
//Unable to get the configuration value
console.log(routeConfigProvider.configuration);
//Console is returning as "undefined"
routeConfigProvider.getRoutes().forEach(function(r) {
$routeProvider.when(r.url, r.config);
});
$routeProvider.otherwise({ redirectTo: '/' });
}
]);
})();
Created a plunkr demo : http://plnkr.co/edit/2TIqgxMxBJEPbnk2Wk6D?p=preview
(Regarding your last comment, with the plnkr)
The result is expected.
At config time (within app.config() ), you access raw providers, as you defined them, which allows you to call "private" methods or fields (testItem1) and to configure it for run time use. "private" because they won't be accessible at run time.
At run time (within app.run() and the rest of your app), when you ask for a dependency for which you wrote a provider, the angular injector hands you the result of the $get method of your provider, not the provider itself, so you can't access the "private" function.
This page was my path to enlightenment : AngularJS: Service vs provider vs factory
I think you may be over complicating the route stuff. You may have a very good reason for it but as I do not know it may I suggest keeping it simple with something more like this:
MyApp.config(function ($routeProvider) {
$routeProvider.when('/home', {
templateUrl: 'home.html',
controller: 'HomeController',
activeTab: 'home'
})
};
MyApp.controller('HomeController', function ($route) {
console.log($route.current.activeTab);
});
I would be interested in knowing why you may not able to use this routing pattern or purposely chose something different.
I think it has to do with the way you are creating your initial module. Try this:
var app = angular.module('app', []);
app.constant('configuration', {
PARTIAL_PATH: "/app/components/partials"
});
var routeServiceModule = angular.module('routeService', ['app']);

Categories