2

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:

[
  {
    "name": "state1",
    "url": "state1",
    "template": "<h1>state1</h1>"
  },
  {
    "name": "state2",
    "url": "state2",
    "template": "<h1>state2</h1>"
  }
]
alfredopacino
  • 2,979
  • 9
  • 42
  • 68

1 Answers1

1

EXTEND:

Based on extended question, there is a working example. The JSON was adjusted like this:

// api/remoteStates.json
[
  {
    "name": "state1",
    "url": "/state1",
    "template": "<h1>state1</h1>"
  },
  {
    "name": "state2",
    "url": "/state2",
    "template": "<h1>state2</h1>"
  }
]

And this is almost unchanged state registration

// script.js - first part CONFIG phase
app.config(function ($locationProvider, $urlRouterProvider, $stateProvider) {

    $stateProvider
     .state('about', {
        url: "/about",
        templateUrl: "partials/about.html"
    })

    $urlRouterProvider.deferIntercept();
    $urlRouterProvider.otherwise('/about');

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

app.run(['$rootScope', '$state', '$stateParams',
  function ($rootScope, $state, $stateParams) {
    $rootScope.$state = $state;
    $rootScope.$stateParams = $stateParams;
}])

and the run part

// script.js - RUN phase
app.run([ '$rootScope','$http', '$urlRouter',
    function ($rootScope, $http, $urlRouter)
    {
        $http
            .get("api/remoteStates.json")
            .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();
            });
    }]);

Check that all here in action

ORIGINAL part 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();
      });
}]);
Community
  • 1
  • 1
Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
  • thanks, it looks like it breaks the normal $stateProvider behaviour, I have those dynamic routes, but I have also several static routes defined with $stateProvider.. – alfredopacino Aug 29 '16 at 08:32
  • It is working together.. static and dynamic. Just.. until the server data are loaded, and sync is called.. the UI-Router is waiting. So, even the dynamic url will be working as expected. I would say, that you should play with this solution.. and it should fit to your needs ;) – Radim Köhler Aug 29 '16 at 08:33
  • I've just realized that since my server data are not-so-lightweight (about 15 extra states and their templates). It would be way better a lazy load technique for states and controllers (maybe with ocLazyLoad), what's your opinion? anyway very thanks your answer solve a problem in another application of mine. – alfredopacino Aug 30 '16 at 15:35
  • 1
    oc lazy load is good way to go. Also, start to check Angular2 and brand new approach with NgModules, which are out of the box (native angular 2 solution) ready for lazy loading. BTW I do use the above approach.. to load about hundred states based on data coming from server. I do not have any issue with performance. Hope it hleps a it. Good luck with UI-Router ;) – Radim Köhler Aug 30 '16 at 15:41
  • I extended my answer, used your question details and created plunker. Please, observe it, and I am sure you will gladly see how easy it is ;) Enjoy mighty UI-Router sir... – Radim Köhler Sep 01 '16 at 05:21