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 Modules like lets say a shoppingList and a calculator, now my goal would be the User logs into my App from the Database I get the User rights and depending what rights he has, i would load the html for the views and the controller files for the logic part from the Server, while doing that I would create the states needed for the html and ctrl. So basically my App is very small consistent of the Login and everything else is getting pulled from the Server depending on the Userrights.
What I use:
- Cordova
- AngularJs
- Ionic Framework
Why I need it to be all dynamic:
1)The possiblity to have an App that contains just the login logic, so when fixing bugs or adding Modules I only have to add the files to the server give the User the right for it and it is there without needing to update the app.
2)The User only has the functionality he needs, he doesnt need to have everything when he only has the right for 1 module.
3)The App grows very big at the moment, meaning every Module has like 5-10 states, with their own html and Controllers. currently there are 50 different Modules planned so you can do the math.
I looked at this to get some inspiration:
AngularJS, ocLazyLoad & loading dynamic States
What I tried so far:
I created 1 Html file which contains the whole module so I only have 1 http request:
Lets say this is my response from the server after the User logged in
HTML Part:
var rights= [A,B,C,D]
angular.forEach(rights, function (value, key) {
$http.get('http://myServer.com/templates/' + value + '.html').then(function (response) {
//HTML file for the whole module
splits = response.data.split('#');
//Array off HTMl strings
for (var l = 1; l <= splits.length; l++) {
//Putting all Html strings into templateCache
$templateCache.put('templates/' + value +'.html', splits[l - 1]);
}
}
});
Controller Part:
var rights= [A,B,C,D]
angular.forEach(rights, function (value, key) {
$http.get('http://myServer.com/controller/' + value + '.js').then(function (response) {
// 1 file for the whole module with all controllers
splits = response.data.split('#');
//Array off controller strings
for (var l = 1; l <= splits.length; l++) {
//Putting all Controller strings into templateCache
$templateCache.put('controllers/' + value +'.js', splits[l - 1]);
}
}
});
After loading the Controllers I try to register them:
$controllerProvider.register('SomeName', $templateCache.get('controllers/someController));
Which is not working since this is only a string...
Defining the Providers:
.config(function ($stateProvider, $urlRouterProvider, $ionicConfigProvider, $controllerProvider) {
// turns of the page transition globally
$ionicConfigProvider.views.transition('none');
$stateProviderRef = $stateProvider;
$urlRouterProviderRef = $urlRouterProvider;
$controllerProviderRef = $controllerProvider;
$stateProvider
//the login state is static for every user
.state('login', {
url: "/login",
templateUrl: "templates/login.html",
controller: "LoginCtrl"
});
//all the other states are missing and should be created depending on rights
$urlRouterProvider.otherwise('/login');
});
Ui-Router Part:
//Lets assume here the Rights Array contains more information like name, url...
angular.forEach(rights, function (value, key) {
//Checks if the state was already added
var getExistingState = $state.get(value.name)
if (getExistingState !== null) {
return;
}
var state = {
'lang': value.lang,
'params': value.param,
'url': value.url,
'templateProvider': function ($timeout, $templateCache, Ls) {
return $timeout(function () {
return $templateCache.get("templates" + value.url + ".html")
}, 100);
},
'ControllerProvider': function ($timeout, $templateCache, Ls) {
return $timeout(function () {
return $templateCache.get("controllers" + value.url + ".js")
}, 100);
}
$stateProviderRef.state(value.name, state);
});
$urlRouter.sync();
$urlRouter.listen();
Situation so far:
I have managed to load the html files and store them in the templateCache, even load them but only if the states were predefined.What I noticed here was that sometimes lets say when I remove an item from a List and come back to the View the item was there again maybe this has something to do with cache I am not really sure...
I have managed to load the controller files and save the controllers in the templateCache but I dont really know how to use the $ControllerPrioviderRef.register with my stored strings...
Creating the states did work but the Controller didnt fit so i could not open any views...
PS: I also looked at require.js and OCLazyLoad as well as this example dynamic controller example
Update:
Okay so I managed to load the Html
, create the State
with the Controller
everything seems to work fine, except that the Controller does not seem to work at all, there are no errors, but it seems nothing of the Controller logic is executed. Currently the only solution to register the controller from the previous downloaded file was to use eval(),
which is more a hack then a proper solution.
Here the code:
.factory('ModularService', ['$http', ....., function ( $http, ...., ) {
return {
LoadModularContent: function () {
//var $state = $rootScope.$state;
var json = [
{
module: 'Calc',
name: 'ca10',
lang: [],
params: 9,
url: '/ca10',
templateUrl: "templates/ca/ca10.html",
controller: ["Ca10"]
},
{
module: 'SL',
name: 'sl10',
lang: [],
params: 9,
url: '/sl10',
templateUrl: "templates/sl/sl10.html",
controller: ['Sl10', 'Sl20', 'Sl25', 'Sl30', 'Sl40', 'Sl50', 'Sl60', 'Sl70']
}
];
//Load the html
angular.forEach(json, function (value, key) {
$http.get('http://myserver.com/' + value.module + '.html')
.then(function (response) {
var splits = response.data.split('#');
for (var l = 1; l <= value.controller.length; l++) {
$templateCache.put('templates/' + value.controller[l - 1] + '.html', splits[l - 1]);
if (l == value.controller.length) {
$http.get('http://myserver.com//'+value.module+'.js')
.then(function (response2) {
var ctrls = response2.data.split('##');
var fullctrl;
for (var m = 1; m <= value.controller.length; m++){
var ctrlName = value.controller[m - 1] + 'Ctrl';
$controllerProviderRef
.register(ctrlName, ['$scope',...., function ($scope, ...,) {
eval(ctrls[m - 1]);
}])
if (m == value.controller.length) {
for (var o = 1; o <= value.controller.length; o++) {
var html = $templateCache
.get("templates/" + value.controller[o - 1] + ".html");
var getExistingState = $state.get(value.controller[o - 1].toLowerCase());
if (getExistingState !== null) {
return;
}
var state = {
'lang': value.lang,
'params': value.param,
'url': '/' + value.controller[o - 1].toLowerCase(),
'template': html,
'controller': value.controller[o - 1] + 'Ctrl'
};
$stateProviderRef.state(value.controller[o - 1].toLowerCase(), state);
}
}
}
});
}
}
});
});
// Configures $urlRouter's listener *after* your custom listener
$urlRouter.sync();
$urlRouter.listen();
}
}
}])
Any help appreciated