alfredopacino alfredopacino - 3 months ago 34
Javascript Question

Dynamic routes from server data

I have some nested routes defined dinamically:

var res = [
{i:1,title:"title 1"},
{i:2,title:"title 2"},
{i:3,title:"title 3"},
{i:4,title:"title 4"}
];

app.config(function($stateProvider, $urlRouterProvider, $authProvider,$httpProvider) {
// other routes [...]
angular.forEach(res, function(d) {
$stateProvider.state('nested.q' + d.i, {
url: '/' + d.i,
template: '{{d.i}} - {{d.title}}'
});
});
});


var res
should be retrieved in ajax from the server, I'm not sure how to accomplish that in this step of the angular workflow (earlier that any service or controller loads).

EDIT based on @Radim Köhler answer I tried this code, what's wrong with this? the routes simply aren't registered:

var $stateProviderRef; // is this global var used like you thought??
app.config(function($stateProvider, $urlRouterProvider,$httpProvider,$locationProvider) {

$stateProvider
.state('about', {
url: "/about",
templateUrl: "/partials/about.html"
})
$urlRouterProvider.deferIntercept();
$locationProvider.html5Mode({enabled: false});
$stateProviderRef = $stateProvider;

$urlRouterProvider.otherwise('/');

});

app.run([ '$rootScope','$http', '$urlRouter',
function ($rootScope, $http, $urlRouter)
{
$http
.get("/api/remoteStates")
.success(function(data)
{
angular.forEach(data, function (value, key)
{
console.log(value)
$stateProviderRef.state(value.name, {
url: value.url,
template: value.template
});
});
$urlRouter.sync();
$urlRouter.listen();
});
}]);


/api/remoteState response:

[
{
"state": "state1",
"url": "state1",
"template": "<h1>state1</h1>"
},
{
"state": "state2",
"url": "state2",
"template": "<h1>state2</h1>"
}
]

Answer

Check this Q & A:

AngularJS - UI-router - How to configure dynamic views

As documented here

$urlRouterProvider

The deferIntercept(defer)

Disables (or enables) deferring location change interception.

If you wish to customize the behavior of syncing the URL (for example, if you wish to defer a transition but maintain the current URL), call this method at configuration time. Then, at run time, call $urlRouter.listen() after you have configured your own $locationChangeSuccess event handler.

There is a working plunker

Wee need to do that in config phase (postpone execution)

app.config(function ($locationProvider, $urlRouterProvider, $stateProvider) {

    // Prevent $urlRouter from automatically intercepting URL changes;
    // this allows you to configure custom behavior in between
    // location changes and route synchronization:
    $urlRouterProvider.deferIntercept();
    $urlRouterProvider.otherwise('/other');

    $locationProvider.html5Mode({enabled: false});
    $stateProviderRef = $stateProvider;
});

Then, we get a data from server($http) in a run phase and once new dynamic states are created.. just call sync

app.run([ '$rootScope','$http', '$urlRouter',
  function ($rootScope, $http, $urlRouter) 
  {
    $http
      .get("myJson.json")
      .success(function(data)
      {
        angular.forEach(data, function (value, key) 
        { 
          var state = {
            "url": ...
            "parent" :  ...
            "abstract":  ...
            "views": { ... }
          };

          angular.forEach(value.views, function (view) 
          {
            state.views[view.name] = {
              templateUrl : view.templateUrl,
            };
          });

          $stateProviderRef.state(value.name, state);
        });
        // Configures $urlRouter's listener *after* your custom listener            
        $urlRouter.sync();
        $urlRouter.listen();
      });
}]);