angularjsangular-ui-routerangular-ui-router-extras

AngularJS state loading


I'm trying to load states dynamically from a JSON file using ui-router-extras. The JSON file looks like the following:

    [
  {
    "name": "app",
    "url": "/app",
    "abstract": "true",
    "resolve": "helper.resolveFor('fastclick', 'modernizr', 'icons')"
    
  },
  {
    "name": "login",
    "url": "/login",
    "title": "User Login",
    "templateUrl": "",
    "redirectToLogin": "true"
  },
  {
    "name": "dashboard",
    "url": "/dashboard",
    "title": "Dashboard",
    "templateUrl": "helper.basepath('dashboard.html')",
    "resolve": "helper.resolveFor('flot-chart', 'flot-chart-plugins', 'weather-icons')"
   
  }
]

The following is the routes config file:

(function () {
'use strict';

angular
    .module('app.routes')
    .config(routesConfig);

routesConfig.$inject = ['$stateProvider', '$locationProvider', '$urlRouterProvider', 'RouteHelpersProvider', '$stateProvider', '$futureStateProvider'];
function routesConfig($stateProvider, $locationProvider, $urlRouterProvider, helper, $sp, $fsp) {

    $locationProvider.html5Mode(false);

    var futureStateResolve = function($http) { 
        return $http.get("states.json").then(function (response) {
            console.log(response.data);
            angular.forEach(response.data, function (state) {

                $sp.state(state);
                })
            })
        }
    $fsp.addResolve(futureStateResolve);

         // defaults to dashboard
    $urlRouterProvider.otherwise('/app/login');
    }
})();

The /login state works ok, but the others don't work and give the error 'invocables must be object'. I think this is because helper.basepath() and helper.resolveFor() functions are not working, as they are coming as strings from the JSON.

What should I do?


Solution

  • 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