Is there any way to make a server hit to retrieve data before setting up all the routes on the $routeProvider? I want to be able to dynamically setup routes based on this remote data. I tried something like this:
angular.module("myApp").config(["$routeProvider", "$http", function($routeProvider, $http) {
$http.get("myData").success(function(data) {
$routeProvider.when(data.dynamicRoute, {
//route definition
}
//or
$routeProvider.when("/known/route", {
redirectTo: data.dynamicRoute
}
});
}]);
but that results in the following error:
Unknown provider: $http from myApp
So, I understand that the config function is injecting providers and not services. However, I still want to know if I can somehow accomplish my end goal? I don't think I could do this with the $httpProvider, but please someone correct me if I'm wrong. If there's some fundamental reason why this just isn't possible, please explain. Any help with this would be much appreciated.
Update: @dnc253 -- angular-detour is now ready enough that I think you could use it to get routes loaded from json. It's not yet well-documented and doesn't have tests, so it's certainly not complete in that sense, but the merging of json into a live instance of the router has been working for a while.
If you're still looking for a way to use server defined routes, you can read instructions on running the samples and then check out the third sample's mergeJson call in app.js. Feedback would be great -- I'm getting started on real documentation/tests now, so there's no need to tell me that the documentation sucks and there aren't any tests -- I'm on that one. :) Hopefully the samples are enough to squeak by if this functionality is still on your list of needs
Older part of the answer: If you just want a static set of routes that are defined by the server with JSON, you could use something like JQuery to fetch your routing data before you configure your app.
Pseudocode:
$.ajax({
url: "http://example.com/routes"
}
}).done(function ( data ) {
angular
.module('myApp',[])
.config(function($routeProvider) {
//read routing instructions from data and call "when" on $routeProvider to define the routes
});
});
This should work with ui-router as well. But it only enables you to fetch an initial set of routes, so while it gives you the possibility of data-driven definitions, they are only driven by the data that was available when the app was loaded by the client.
As eazel7 points out, you can access providers at run time if you create your own service that binds to the provider at config time. However, to do what you want to do you need to go out of process to do an $http call, and with the current routers (the stock Angular core and ui-router) you won't get your answer in time to populate the router before a deep link fails when someone visits your site. Additionally both of those routers use functions to populate the routing "table" and thus do not lend themselves to having the routing updated after it has been initially configured.
I'm working on something that addresses these issues, such that if a requested route is unknown to the router, it will attempt to fetch it from the server before invoking the fallback route or failing. Also will support checking for modifications to known routes. Essentially lazy routing with an AJAX/JSON interface to the server. It's based on ui-router, and is living at https://github.com/afterglowtech/angular-detour . It currently implements the editability of routes but I haven't done the laziness/server communication pieces. That's next on my list. Keep an eye on it, it should end up doing what you're looking for.
There is no way to do this.
A provider is a configurable service creator. They allow us to provide an API to use to configure their creation. And the configurability is what brings us to config blocks. These are used before services are available in order to configure their creation. So you can pass settings to $routeProvider or $httpProvider that it will use when it creates the $route and $http services, respectively.
Because the services are still being configured at this stage, the services themselves are not available for injection - they don't actually exist yet.
The $routeProvider allows us to configure routes, which must be running when our application starts. It wouldn't make sense to have routing start at some random point during the running of our application.
All this is to say that you can't start running your application, which is what using $http would mean, until after the configuration is done. So there's no way to define routes from remote data.
For what it's worth, I don't think I see the value in such dynamic routes anyway; they would be unpredictable by nature because they are built from data that is not static. This would totally break bookmarkability, which is the principal purpose of routing.
So I would ask why you feel the need to do this in the first place and then take a step back and see if this is really even the way it should ideally be done.
Feel free to post a comment to provoke further discussion.
I think the easiest way to do such thing is to resolve the routes later, you could ask the routes via json, for example. Check out that I make a factory out of the $routeProvider during config phase, via $provide, so I can keep using the $routeProvider object in the run phase, and even in controllers.
'use strict';
angular.module('myapp', []).config(function($provide, $routeProvider) {
$provide.factory('$routeProvider', function () {
return $routeProvider;
});
}).run(function($routeProvider, $http) {
$routeProvider.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
}).otherwise({
redirectTo: '/'
});
$http.get('/dynamic-routes.json').success(function(data) {
$routeProvider.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
});
// you might need to call $route.reload() if the route changed
$route.reload();
});
});
This is 100% working solution!
Prepare your routes. They might be looked like this ones in json
{"0":{"id":1,"when":"/","controller":"MainController","template":"app/views/site/main_inner.html"},"1":{"id":2,"when":"/firm/:firmID","controller":"CompanyController","template":"app/views/site/firm.html"},"2":{"id":3,"when":"/search","controller":"SearchController","template":"app/views/site/search.html"}}
First, you have to make a global reference to RouteProvider
var $routeProviderReference;
var app = angular.module('myApp', []);
app.config(['$routeProvider', function($routeProvider) {
$routeProviderReference = $routeProvider;
}]);
Second, use method run to do an ajax call for routes.
app.run(['$rootScope', '$http', '$route', function($rootScope, $http, $route) {
//getting routes
$http.get('routes.php').success(function (data) {
angular.forEach(data, function (route) {
$routeProviderReference.when( route.when, { templateUrl: route.template, controller: route.controller } );
});
$routeProviderReference.otherwise({ redirectTo: '/' });
$route.reload();
});
}]);
For more info look at the ref bellow http://blog.brunoscopelliti.com/how-to-defer-route-definition-in-an-angularjs-web-app