Loading html and Controller from server and creating dynamic states UI - router

前端 未结 4 1905
后悔当初
后悔当初 2021-02-05 11:34

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

4条回答
  •  野性不改
    2021-02-05 12:11

    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.

提交回复
热议问题