Angularjs - UI Router - Programmatically Add States

AngularJS - UI Router - programmatically add states

See -edit- for updated information

Normally states are added to the $stateProvider during the config phase. If you want to add states at runtime, you'll need to keep a reference to the $stateProvider around.

This code is untested, but should do what you want. It creates a service called runtimeStates. You can inject it into runtime code and then add states.

// config-time dependencies can be injected here at .provider() declaration
myapp.provider('runtimeStates', function runtimeStates($stateProvider) {
// runtime dependencies for the service can be injected here, at the provider.$get() function.
this.$get = function($q, $timeout, $state) { // for example
return {
addState: function(name, state) {
$stateProvider.state(name, state);
}
}
}
});

I've implemented some stuff called Future States in UI-Router Extras that take care of some of the corner cases for you like mapping urls to states that don't exist yet. Future States also shows how you can lazy load the source code for runtime-states. Take a look at the source code to get a feel for what is involved.

-edit- for UI-Router 1.0+

In UI-Router 1.0, states can be registered and deregistered at runtime using StateRegistry.register and StateRegistry.deregister.

To get access to the StateRegistry, inject it as $stateRegistry, or inject $uiRouter and access it via UIRouter.stateRegistry.

UI-Router 1.0 also includes Future States out of the box which handles lazy loading of state definitions, even synchronizing by URL.

AngularJS - UI-router - How to configure dynamic views

There is a plunker showing how we can configure the views dynamically. The updated version of the .run() would be like this:

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

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

$stateProviderRef.state(value.name, state);
});
$state.go("home");
});
}]);

Check that all in action here

AngularJS state loading

My advice is the same as @charlietfli. I will elaborate.

Since you have already created a helper function that returns your resolve block, you can easily work around the fact that JSON doesn't support JS code or expressions.

Your helper function takes a list of strings as arguments. JSON supports arrays of strings. Change your JSON from this: "resolve": "helper.resolveFor('fastclick', 'modernizr', 'icons')" to this: "resolve": ["fastclick", "modernizr", "icons"]. Now, the JSON resolve contains a simple array of strings.

When you load the states, first convert the array to a resolve block using your helper, and then register them.

var futureStateResolve = function($http) { 
return $http.get("states.json").then(function (response) {
angular.forEach(response.data, function (state) {
// The array of strings from the JSON
var stringArgs = state.resolve;
// If present, convert to a resolve: object and put back on the state definition
if (stringArgs) state.resolve = helper.resolveFor.apply(helper, stringArgs);
$sp.state(state);
})
})
}
$fsp.addResolve(futureStateResolve);

That should answer your question.

However, I noticed you are using future states from ui-router-extras, but you are not lazy loading the code (controllers or whole javascript angular modules) itself. Normally, you register future states with the $futureStateProvider, and not directly with the $stateProvider. There's nothing wrong with what you are doing (lazy loading the state definitions and taking advantage of deferred ui-router bootstrap from Future States). However, you could achieve the same thing without depending on the ui-router-extras library.

This is how you would lazily add state definitions without ui-router-extras:

Angular - UI Router - programmatically add states

This is how you can defer ui-router from parsing the url until you are done adding state definitions:

Angular ui-router and ocLazyLoad

$state.go('state.name') not correctly changing states

2 things:

  1. You forgot to inject $state into your app.run()
  2. Use $stateChangeSuccess instead

    .run(['$rootScope','$state', function($rootScope, $state) {
    $rootScope.$on('$stateChangeSuccess', function(event, next) {
    $state.go('root.other');
    })}]);

Working plunk.

Angular ui-router dynamically add resolve function to all states

I've solved the issue in this way:

angular.module('sample', ['ui.router']).config(function($stateProvider) {
var $delegate = $stateProvider.state;

$stateProvider.state = function(name, definition) {
definition.resolve = angular.extend({}, definition.resolve, {
myCustomResolve: function() { return true; }
});

return $delegate.apply(this, arguments);
};
$stateProvider.state('example', { url: 'example/' });

}).run(function($state) { console.log('have resolve?', $state.get('example')); });


Related Topics



Leave a reply



Submit