8
\$\begingroup\$

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.

\$\endgroup\$
1
\$\begingroup\$

I don't think Angular has such CMS functions. Writing your own was probably the right call.

The little code you provided is well written, JsHint could not find any fault and it is well commented.

We can give more valuable feedback when you provide unsanitized code samples ;)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.