While trying to create an "admin" backend (to allow for CMS like functionality) to a site using AngularJS on the frontend, I came across an issue with routes.
In order to allow the admin to create new content, ie install new component (view/controller), change menu item href locations etc, I would have to go in and edit the code that defines the $routeProvider
routes manually.
So normally you would do(with angular's own route module):
$routeProvider.
when('/somepage', {
templateUrl: '/templates/somepage.html',
controller: 'SomeCtrl'
}).
when('/otherpage', {
templateUrl: '/templates/someotherpage.html',
controller: 'SomeOtherCtrl'
});
So if a new component was installed that wanted to say use /Gallery
route i would have to go in and add in
when('/Gallery',...)
So in order to solve this I added in lazy loading capabilities, but even still I would have to go in and add in the route manually. So I modified the resolve
deps
function to check the server to see if there were any defined routes (ie defined in a menu database, or other places), if it finds a defined route it then returns the dependencies which are then lazy loaded and then add the components routes to the $routeProvider
and then trigger a $locationChangeSuccess
so that it will reprocess the url path.
app.controllerProvider = $controllerProvider.register;
app.compileProvider = $compileProvider.register;
app.routeProvider = $routeProvider;
$routeProvider.
when('/404',{
templateUrl:"/templates/404.html",
controller:"PageErrorCtrl"
}).
//Catchall for all other paths
otherwise({
resolve:{
deps:function($q, $rootScope, $location) {
var deferred = $q.defer();
var path = $location.path();
//Code to prevent infinite loop
...
//Make a call to server backend find if there is a defined route for this path
//Or if there is a custom route for this path
getRouteDependencies(path,function(err,data){
if(err){ //No route found
$location.path("/404");
return;
}
//JSLoader is a custom function to load in the dependencies
//ie add script tag to head
JSLoader.load(data.dependencies, function() {
$rootScope.$apply(function() {
//call deffered.resolve()?
//reprocess the route
$rootScope.$broadcast('$locationChangeSuccess', path, path);
});
});
});
return deferred.promise;
}
}
});
So if the admin adds a Gallery template/controller that had a route defined as /Gallery
defined in the database, and a user goes to http://example.com/Gallery
the otherwise
part of $routeProvider
would be triggered and in effect trigger the resolve function which would then load in the dependencies, ie gallery.js:
app.controllerProvider('GalleryCtrl',['$scope','$route','$routeParams',
function ($scope,$route,$routeParams) {
$scope.data = {};
$scope.actions = {};
$scope.events = {};
}
]);
//TODO: modify to allow changing of base path (ie /Gallery part) dynamically
app.routeProvider.
when('/Gallery/:galleryId', {
templateUrl: '/templates/gallery.html',
controller: 'GalleryCtrl'
}).
when('/Gallery/:galleryId/:imageId', {
templateUrl: '/templates/galleryImage.html',
controller: 'GalleryCtrl'
});
So after the loader loads in the above code the new routes are defined on $routeProvider
and then the
$rootScope.$broadcast('$locationChangeSuccess', path, path);
call in the resolve function will tell angular to reprocess the url, which angular should then see as having a route and execute the controller and load in the template associated with that route.
But I was wondering if there is already a part of angular that is supposed to handle this type of scenario( tried searching but searches i tried just returned lazy loading stuff ), a better way of doing it, or if there is something wrong with my approach that would need changing?
There is this question on Stack Overflow, but most of the answers are for just loading in the html templates, and do not take into account the need to load different controllers that have not been already defined.