I am looking for a Solution to load my App Content dynamically from the Server.
My Scenario:
Lets say we have 2 Users (A and B), my App consists of different Mod
Can I suggest you to do some changes to the way you load the states?
Write a script that give you back a json with the states the user can access.
Ex.
resources/routing-config.yourLangage?user=user-id-12345
this will return a json file that depends on the user logged in. The structure can be something like this:
[
{
"name": "home",
"url": "/home",
"templateUrl": "views/home.html",
"controller": "HomeController",
"dependencies": ["scripts/home/controllers.js", "scripts/home/services.js", "scripts/home/directives.js"]
},
{
"name": "user",
"url": "/user",
"templateUrl": "views/user.html",
"controller": "UserController",
"dependencies": ["scripts/user/controllers.js", "scripts/user/services.js", "scripts/home/directives.js"]
}
]
Then let's write a service that will read the states the user is allowed to access:
app.factory('routingConfig', ['$resource',
function ($resource) {
return $resource('resources/routing-config.yourLangage', {}, {
query: {method: 'GET',
params: {},
isArray: true,
transformResponse: function (data) {
// before that we give the states data to the app, let's load all the dependencies
var states = [];
angular.forEach(angular.fromJson(data), function(value, key) {
value.resolve = {
deps: ['$q', '$rootScope', function($q, $rootScope){
// this will be resolved only when the user will go to the relative state defined in the var value
var deferred = $q.defer();
/*
now we need to load the dependencies. I use the script.js javascript loader to load the dependencies for each page.
It is very small and easy to be used
http://www.dustindiaz.com/scriptjs
*/
$script(value.dependencies, function(){ //here we will load what is defined in the dependencies field. ex: "dependencies": ["scripts/user/controllers.js", "scripts/user/services.js", "scripts/home/directives.js"]
// all dependencies have now been loaded by so resolve the promise
$rootScope.$apply(function(){
deferred.resolve();
});
});
return deferred.promise;
}]
};
states.push(value);
});
return states;
}
}
});
}]);
Then let's configure the app:
app.config(['$stateProvider', '$urlRouterProvider', '$locationProvider', '$filterProvider', '$provide', '$compileProvider',
function ($stateProvider, $urlRouterProvider, $locationProvider, $filterProvider, $provide, $compileProvider) {
// this will be the default state where to go as far as the states aren't loaded
var loading = {
name: 'loading',
url: '/loading',
templateUrl: '/views/loading.html',
controller: 'LoadingController'
};
// if the user ask for a page that he cannot access
var _404 = {
name: '_404',
url: '/404',
templateUrl: 'views/404.html',
controller: '404Controller'
};
$stateProvider
.state(loading)
.state(_404);
// save a reference to all of the providers to register everything lazily
$stateProviderRef = $stateProvider;
$urlRouterProviderRef = $urlRouterProvider;
$controllerProviderRef = $controllerProvider;
$filterProviderRef = $filterProvider;
$provideRef = $provide;
$compileProviderRef = $compileProvider;
//redirect the not found urls
$urlRouterProvider.otherwise('/404');
}]);
Now let's use this service in the app.run:
app.run(function ($location, $rootScope, $state, $q, routingConfig) {
// We need to attach a promise to the rootScope. This will tell us when all of the states are loaded.
var myDeferredObj = $q.defer();
$rootScope.promiseRoutingConfigEnd = myDeferredObj.promise;
// Query the config file
var remoteStates = routingConfig.query(function() {
angular.forEach(remoteStates, function(value, key) {
// the state becomes the value
$stateProviderRef.state(value);
});
// resolve the promise.
myDeferredObj.resolve();
});
//redirect to the loading page until all of the states are completely loaded and store the original path requested
$rootScope.myPath = $location.path();
$location.path('/loading'); //and then (in the loading controller) we will redirect to the right state
//check for routing errors
$rootScope.$on('$stateChangeError',
function(event, toState, toParams, fromState, fromParams, error){
console.log.bind(console);
});
$rootScope.$on('$stateNotFound',
function(event, unfoundState, fromState, fromParams){
console.error(unfoundState.to); // "lazy.state"
console.error(unfoundState.toParams); // {a:1, b:2}
console.error(unfoundState.options); // {inherit:false} + default options
});
});
Eventually, the LoadingController:
app.controller('LoadingController', ['$scope', '$location', '$rootScope',
function($scope, $location, $rootScope) {
//when all of the states are loaded, redirect to the requested state
$rootScope.promiseRoutingConfigEnd.then(function(){
//if the user requested the page /loading then redirect him to the home page
if($rootScope.myPath === '/loading'){
$rootScope.myPath = '/home';
}
$location.path($rootScope.myPath);
});
}]);
In this way everything is super flexible and lazy loaded.
I wrote 3 different user portals already and I can easily scale to all of the user portal I want.