I have followed this post How inject $stateProvider in angular application? and I have managed to figure out how to use '$stateprovider' but now I have issue with the '$urlRouterProvider'.
Does that mean I can't inject '$urlRouterProvider' into controller either and it should be only injected into config?
I highly appreciate any help any help on this issue.
There is a very narrowed snippet of the $urlRouterProvider code:
$UrlRouterProvider.$inject = ['$locationProvider', '$urlMatcherFactoryProvider'];
function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
// I. config
// these are CONFIGURATION methods
// we can access in .config() phase
....
this.rule = function (rule) {
...
}
...
this.when = function (what, handler) {
...
}
...
// II. application run
// this is the service/factory/configured provider
// injected in the .run() phase via IoC
this.$get = $get;
$get.$inject = ['$location', '$rootScope', '$injector', '$browser'];
function $get( $location, $rootScope, $injector, $browser) {
...
return {
sync: function() {
...
},
listen: function() {
...
}
And this is the answer. In the config we do have access to configuration methods of the provider $urlRouterProvider. While later, in phase of application run, our services/factories are via IoC provided with the result of the configured $get()
For more details, see:
Configuring Providers
You may be wondering why anyone would bother to set up a full-fledged provider with the provide method if factory, value, etc. are so much easier. The answer is that providers allow a lot of configuration. We've already mentioned that when you create a service via the provider (or any of the shortcuts Angular gives you), you create a new provider that defines how that service is constructed. What I didn't mention is that these providers can be injected into config sections of your application so you can interact with them!
First, Angular runs your application in two-phases--the config and run phases. The config phase, as we've seen, is where you can set up any providers as necessary. This is also where directives, controllers, filters, and the like get set up. The run phase, as you might guess, is where Angular actually compiles your DOM and starts up your app.
In case, you need to access $stateProvider or $routeProvider configuration even later... there is a way... Check this plunker. Can hardly say that this is the angular way... but it is working. Check it here
AngularJS - UI-router - How to configure dynamic views
http://plnkr.co/edit/I4AGHa3xgfv8WUMotxNL?p=preview
Related
I have a problem.
I wants design multiple modules, each module will config route for itself. And before login I just wants load 3 modules (with 3 route), and after login I continue load 3 module (with 3 route). So I need re-config myApp to add new routes, then re-set into body to apply myApp.
And my solution is:
I create mainModule is myApp, and i config for it then I set into body by angular.bootstrap
angular.element(document).ready(function() {
angular.bootstrap(document.body, ["myApp"]);
});
Then, I need re-config for myApp and I set again.
angular.element(document).ready(function() {
angular.bootstrap(document.body, ["myApp"], true);
});
An error occurs
[ng:btstrpd] App Already Bootstrapped with this Element '<body cz-shortcut-listen="true" class="ng-scope">'(…)
How can I reset myApp into body or any solution ???
Thanks,
While lazy loading is not among listed features in Angular, some of the things which are intrinsic to config phase can be performed at run-time (while they cannot be recommended and belong to 'use at your own risk' category).
When provider is being used to configure the service before its instantiation, most likely (it depends on service implementation) it can be used to configure it after it was instantiated, e.g.
app.config(function ($routeProvider, $provide) {
// now $routeProvider is available for injection during both config and run phases
$provide.constant('$routeProvider', $routeProvider);
});
app.run(function ($routeProvider, $location) {
$routeProvider.when('/brand-new-route', { ... });
$location.path('/brand-new-route');
});
More of this injector trick here.
This method isn't forbidden but relies on current service implementation and undocumented behaviour, so it has to be either tested thoroughly or should be avoided at all.
I'm certainly missing some fundamental point about the injector, but I fail to understand why exactly this
angular.module('app').config(function ($provide) {
...
});
and this
angular.module('app').config(function ($injector) {
$injector.invoke(function ($provide) { ... });
});
work as intended, while this
app.run(function($provide) {
...
});
will throw
Error: [$injector:unpr] Unknown provider: $provideProvider <- $provide
As follows from the above, config has some special relationship with providers, while run deals with instances, yet I'm unsure about the thing that makes config blocks so special.
As a consequence of that, is there no way to get to $provide outside config blocks, e.g. with angular.injector() (though it seems that it gets provider instances also)?
The question, besides mere curiosity, also has some practical considerations. In 1.4 all of $provide functions are exposed to module, but that's not true for 1.3.
The purpose of the config() function is to allow you to perform some global configuration that will affect the entire application - that includes services, directives, controllers, etc. Because of that, the config() block must run before anything else. But, you still need a way to perform the aforementioned configuration and make it available to the rest of the app. And the way to do that is by using providers.
What makes providers "special" is that they have two initialization parts, and one of them is directly related to the config() block. Take a look at the following code:
app.provider('myService', function() {
var self = {};
this.setSomeGlobalProperty = function(value) {
self.someGlobalProperty = value;
};
this.$get = function(someDependency) {
this.doSomething = function() {
console.log(self.someGlobalProperty);
};
};
});
app.config(function(myServiceProvider) {
myServiceProvider.setSomeGlobalProperty('foobar');
});
app.controller('MyCtrl', function(myService) {
myService.doSomething();
});
When you inject a provider into the config() function, you can access anything but the $get function (technically you can access the $get function, but calling it won't work). That way you can do whatever configuration you might need to do. That's the first initialization part. It's worth mentioning that even though our service is called myService, you need to use the suffix Provider here.
But when you inject the same provider into any other place, Angular calls the $get() function and injects whatever it returns. That's the second initialization part. In this case, the provider behaves just like an ordinary service.
Now about $provide and $injector. Since they are "configuration services", it makes sense to me that you can't access them outside the config() block. If you could, then you would be able to, say, create a factory after it had been used by another service.
Finally, I haven't played with v1.4 yet, so I have no idea why that behavior apparently has changed. If anyone knows why, please let me know and I'll update my answer.
After some Angular injector study I was able to give an exhaustive answer to my own question.
Essentially, $injector in config blocks and provider constructor functions and $injector everywhere else are two different services with the same name, which are defined on internal provider/instance cache explicitly, together with $provide (this one is being defined in provider cache, hence it can be injected in config only).
While generally not recommended because of probable race conditions, it is possible to expose internal services to instance cache and make config-specific $provide and $injector available for injection after config phase has ended:
app.config(function ($provide, $injector) {
$provide.value('$providerInjector', $injector);
$provide.value('$provide', $provide);
});
The possible applications are configuring service providers any time (if possible)
app.run(function ($providerInjector) {
var $compileProvider = $providerInjector.get('$compileProvider');
...
});
and defining new components at run-time
app.run(function ($provide) {
$provide.controller(...);
...
});
First of all, this is an honest question. And I'm looking for honest and justified answers on why I shouldn't be doing this...
angular
.module('X', ['Y'])
.config(function (myFactoryProvider, myServiceProvider) {
myFactoryProvider.$get().myFn();
myServiceProvider.$get().myFn();
});
angular
.module('Y', [])
.factory('myFactory', ['$location', function ($location) {
return {
myFn: function () {
console.log('factory');
console.log($location.absUrl());
}
}
}])
.service('myService', ['$location', function ($location) {
this.myFn = function () {
console.log('service');
console.log($location.absUrl());
}
}]);
Here's a JSFiddle: http://jsfiddle.net/1vetnu6o/
This is working as you can see above and it solves a few problems for me. But I shouldn't probably be doing this and I want to understand why. I really need good reasons to not do this. Despite the fact that I really want to.
tl;dr;
Here's the context of the problem...
I have this internal framework used by multiple products where there's this one service (which happens to be a factory) that basically contains a group of related helper methods. In this case device related like isMobileDevice, isAndroid, getDeviceType (with returns mobile, tablet or desktop), and few others...
This service must be injected into the config() phase of the application using the framework because we need access to the getDeviceType function. The thing is, we need to get the deviceType to load proper templates with $routeProvider. It's in the config() phase that we are building the correct template paths to be used for all the routes. Some of them depend on the deviceType, while others have a generic template independent of the device.
Since this is a service, we cannot inject it directly into the config() phase but we can call that method using the technique I mentioned earlier in the post.
How I'm currently solving this? The helper service is actually a provider and all the methods are exposed both in the provider section as well as in the factory function. Not ideal, but it works. I consider this a work-around, but I'd rather have a work-around in the application and not the framework, thus the technique first mentioned.
Thoughts?
I didn't knew but actually you can invoke on config phase.
The problem is that myService will be instantiated twice :/
angular
.module('X', [])
.config(function(myServiceProvider) {
myServiceProvider.$get().myFn();
})
.run(function(myService) {
myService.myFn();
})
.service('myService', ['$location', function($location) {
console.log('myService!');
this.myFn = function() {
console.log($location.absUrl());
}
}]);
output:
"myService!"
"location"
"myService!"
"location"
This is dangerous if you call $get on config of a service that has a big nested dependency tree :/
I think the correct approach, if you need a utility service (with no dependency), is to use a constant (in this way you can inject it everywhere). Otherwise if you need dependencies use a service and stick to the run() block.
The config() block it's the place to instruct your services how they should work with the help of their providers.
The run() block it's the perfect place to do some logic when your app starts (aka main method).
So this is really weird, maybe it has a simple answer I'm missing. The following code gives an unknown provider error:
var foo = angular.module('foo', [ 'ngRoute', 'ngAnimate', 'ngCookies' ]);
foo.factory('fooApi', function ($scope, $http) {
var url = '/api/';
var factory = {};
factory.action = function (fields) {
fields.userid = $scope.userid;
fields.token = $scope.token;
console.log(JSON.stringify(fields));
return $http.post(url, { data: fields });
};
return factory;
})
.controller('loginController', function ($scope, fooApi) {
// do stuff
});
It's all loading together in the same file, and I'd think the factory being first would resolve and the injector would be able to find it when referenced below. But it gives an unknown provider error.
However, if I comment out the controller and wait for the page to load and then do the exact same controller declaration in the Chrome JS console it works fine.
Anyone run into this before and know how to deal with it? I haven't been able to find this same exact issue anywhere.
Like #tasseKATT said, you can not inject $scope into a service, particularly a factory. Maybe your confusion is because $scope can be injected in controllers, so you tried to injected into a factory.
An interesting thing is that the $scope that you see being injected into controllers is not a service - like the rest of the injectable stuff -, but is a Scope object.
The main purpose of $scope is a king of glue between views and controllers, it doesn't make much sense to pass a $scope into a service.
The services only have access to the $rootScope service.
If you need to pass the $scope of a specific controller to a service always you can pass it like parameter of a function in the service. This approach is not recommended because starting to break the SoC and the single responsibility principle, but maybe could fit you.
Good luck :-)
I've got the guts of a routing architecture in Angular that will dynamically download and inject Angular Controllers and Services ... the Controller part works fine, and I'm trying to download dependant services via $route's .resolve property.
Now, say if I have a factory declared in scope while the page starts up, it registers fine and Controllers that use it resolve fine, e.g:
myModule.factory('MyInjectedDep', function() {
return {};
});
....
MyController = function(MyInjectedDep)
But if I try and register that dependency at "run time" (for want of a better phrase), I get a Circular Dependency error. e.g:
$route.routes[routeItem.route] = {
resolve: {
MyInjectedDep: ['$injector', function($injector) {
// In real code I download/eval this via $http but same behavior occurs
myModule.factory('MyInjectedDep', function() {
return {};
});
}]
}
}
So when my Controller is then initiated:
MyController = function(MyInjectedDep)
I get a circular dependency error, but no dependency trace in the error message?
Error: Circular dependency:
Any ideas appreciated
The key is latching onto $provide at configuration time. If you grab $provide at configuration time and maintain a reference to it, you can use it to register your factory like:
$provide.factory.apply(null, ['MyInjectedDep', [function() {
return {};
]}]);
I have a provider/service designed to do this, adapted from some other samples on github: https://github.com/afterglowtech/angular-couchPotato .
It's primarily designed to load from AMD, but you could probably use it's registerXXX functions with $http, or at least copy the relevant portions of its code. Don't let the size of the repository fool you -- the actual provider/service is about one page of code https://github.com/afterglowtech/angular-couchPotato/blob/master/src/couchPotato.js .