Javascript 从服务器加载html和控制器并创建动态状态UI-router

Javascript 从服务器加载html和控制器并创建动态状态UI-router,javascript,angularjs,requirejs,angular-ui-router,oclazyload,Javascript,Angularjs,Requirejs,Angular Ui Router,Oclazyload,我正在寻找从服务器动态加载我的应用程序内容的解决方案 我的设想: 假设我们有2个用户(A和B),我的应用程序由不同的模块组成,比如购物清单和计算器,现在我的目标是用户从数据库登录到我的应用程序,我获得用户权限,根据他拥有的权限,我将从服务器加载视图的html和逻辑部分的控制器文件,在这样做的同时,我将创建html和ctrl所需的状态。因此,基本上我的应用程序是非常小的登录,其他一切都是从服务器上拉取决于用户权限 我使用的是: 科尔多瓦 安格拉斯 离子骨架 为什么我需要它充满活力: 1) 有可能有

我正在寻找从服务器动态加载我的应用程序内容的解决方案

我的设想:

假设我们有2个用户(A和B),我的应用程序由不同的模块组成,比如购物清单和计算器,现在我的目标是用户从数据库登录到我的应用程序,我获得用户权限,根据他拥有的权限,我将从服务器加载视图的html和逻辑部分的控制器文件,在这样做的同时,我将创建html和ctrl所需的状态。因此,基本上我的应用程序是非常小的登录,其他一切都是从服务器上拉取决于用户权限

我使用的是:

  • 科尔多瓦
  • 安格拉斯
  • 离子骨架
  • 为什么我需要它充满活力:

    1) 有可能有一个只包含登录逻辑的应用程序,因此在修复bug或添加模块时,我只需将文件添加到服务器,就可以为用户提供访问权限,而无需更新应用程序

    2) 用户只拥有他需要的功能,当他只有一个模块的权限时,他不需要拥有所有的功能

    3) 目前该应用程序的规模非常大,这意味着每个模块都有5-10个状态,有自己的html和控制器。目前有50个不同的模块计划,所以你可以做的数学

    我看了这篇文章,获得了一些灵感:

    到目前为止,我尝试的是:

    我创建了一个包含整个模块的Html文件,因此我只有一个http请求:

    假设这是用户登录后服务器的响应

    HTML部分:

    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]);
    
              }
            }
         });
    
    这不起作用,因为这只是一个字符串

    定义提供者:

    .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');
    
    
    });
    
    用户界面路由器部分:

    //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();
    
    到目前为止的情况:

    我已经设法加载html文件并将它们存储在templateCache中,甚至加载它们,但前提是状态是预定义的。我在这里注意到的是,有时让我们说,当我从列表中删除一个项目并返回到视图时,该项目再次出现,可能这与缓存有关我不太确定

    我已设法加载控制器文件并将控制器保存在templateCache中,但我真的不知道如何将$ControllerPrioviderRef.register与存储的字符串一起使用

    创建状态确实有效,但控制器不适合,因此我无法打开任何视图

    PS:我还研究了require.js和OCLazyLoad以及这个示例

    更新:

    好的,所以我设法加载了
    Html
    ,用
    控制器创建了
    状态
    ,一切似乎都很正常,只是控制器似乎根本不工作,没有错误,但似乎没有执行任何控制器逻辑。目前,从先前下载的文件中注册控制器的唯一解决方案是使用
    eval(),
    ,这与其说是一个合适的解决方案,不如说是一种黑客行为

    代码如下:

    .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();
    
            }
        }
    }])
    
    .factory('ModularService',['$http',..,函数($http,…){
    返回{
    LoadModulerContent:函数(){
    //var$state=$rootScope.$state;
    var json=[
    {
    模块:“计算”,
    名称:“ca10”,
    朗:[],
    参数:9,
    url:“/ca10”,
    templateUrl:“templates/ca/ca10.html”,
    控制器:[“Ca10”]
    },
    {
    模块:“SL”,
    名称:“sl10”,
    朗:[],
    参数:9,
    url:“/sl10”,
    templateUrl:“templates/sl/sl10.html”,
    控制器:['Sl10','Sl20','Sl25','Sl30','Sl40','Sl50','Sl60','Sl70']
    }
    ];
    //加载html
    forEach(json,函数(值,键){
    $http.get('http://myserver.com/“+value.module+”.html')
    .然后(功能(响应){
    var splits=response.data.split(“#”);
    
    对于(var l=1;lOk,让我们从头开始

    所有应用程序逻辑都应包含在服务器上,并通过REST、SOAP或类似方式通过API调用提供服务。这样做可以减少UI中内置的逻辑量,从而减少对客户端的压力。这基本上使客户端应用程序成为一个呈现代理,只包含b服务的数据和逻辑的模型和视图AckendAPI

    正如foreyez在评论中所说,这不是任何现代(或半现代)设备的问题


    如果你坚持不一次加载所有的布局,你当然可以将它们分割成部分,然后根据用户权限在登录后加载。这样做,你可以减少内存中的数据量,即使这种改进充其量是值得怀疑的。

    我开发了一个应用程序,将这些数据保存在m中这是我的建筑

    文件夹结构:

    WebApp
    |---CommonModule
        |---common-module.js //Angular Module Defination
        |---Controllers     //Generally Nothing, but if you have a plan to
                            //extend from one CommonController logic to several 
                            //module then it is usefull
    
        |---Services        //Common Service Call Like BaseService for all $http 
                            //call, So no Module Specific Service will not use 
                            //$http directly. Then you can do several common 
                            //things in this BaseService. 
                            //Like Error Handling, 
                            //CSRF token Implementation, 
                            //Encryption/Decryption of AJAX req/res etc.
    
        |---Directives      //Common Directives which you are going to use 
                            //in different Modules
        |---Filters         //Common Filters
    
        |---Templates       //Templates for those common directives
    
        |---index.jsp       //Nothing, Basically Redirect to 
                            //Login or Default Module
    
        |---scripts.jsp     //JQuery, AngularJS and Other Framworks scripts tag.
                            //Along with those, common controlers, services, 
                            //directives and filtes. 
    
        |---templates.jsp   //Include all common templates.
    
        |---ng-include.jsp  //will be used in templates.jsp to create angular 
                            //template script tag.
    |---ModuleA
        |---moduleA-module.js //Angular Module Definition, 
                              //Use Common Module as Sub Module
        |---Controllers
        |---Services
        |---Directives
        |---Filters
        |---Templates
        |---index.jsp 
        |---scripts.jsp 
        |---templates.jsp
    |---ModuleB
        |--- Same as above ...
    
    注意:大写字母表示文件夹。模块a旁边会有一个用于您案例的LoginModule,我想您也可以使用CommonModule

    梅胡的发言如下

    <a href="/ModuleA/">Module A</a> <!--Note: index.jsp are indexed file 
                                     //for a directive -->
    <a href="/ModuleB/">Module B</a>
    
    angular.module('ModuleA.controllers', []);
    angular.module('ModuleA.services', []);
    angular.module('ModuleA.directives', []);
    angular.module('ModuleA.filters', []);
    angular.module('ModuleA', 
           ['Common', 
            'ModuleA.controllers' , 
            'ModuleA.services' , 
            'ModuleA.directives' , 
            'ModuleA.filters'])
        .config(['$routeProvider', function($routeProvider) {
            //$routeProvider state setup
        }])
        .run (function () {
    
        });
    

    我可以建议您更改加载状态的方式吗?
    编写一个脚本,返回一个json,其中包含用户可以访问的状态。
    例如
    资源/路由配置.yourLangage?user=user-id-12345
    这将返回一个json文件,该文件取决于登录的用户。结构可以如下所示:

        [
          {
            "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"]
          }
        ]
    
    然后,让我们编写一个服务来读取允许用户访问的状态:

    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;
                    }
                }
        });
      }]);
    
    然后,让我们配置应用程序:

    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');
    
      }]);
    
    现在让我们在应用程序中使用此服务。运行:

    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
      });
    
    });
    
    最后,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);
        });
    
    }]);
    
    通过这种方式,一切都是超级灵活和惰性加载的

    我已经编写了3个不同的用户门户,我可以轻松地扩展到我想要的所有用户门户。

    我想我正在做你想做的事情
    angular.module('ModuleA.controllers', []);
    angular.module('ModuleA.services', []);
    angular.module('ModuleA.directives', []);
    angular.module('ModuleA.filters', []);
    angular.module('ModuleA', 
           ['Common', 
            'ModuleA.controllers' , 
            'ModuleA.services' , 
            'ModuleA.directives' , 
            'ModuleA.filters'])
        .config(['$routeProvider', function($routeProvider) {
            //$routeProvider state setup
        }])
        .run (function () {
    
        });
    
        [
          {
            "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"]
          }
        ]
    
    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;
                    }
                }
        });
      }]);
    
    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');
    
      }]);
    
    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
      });
    
    });
    
    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);
        });
    
    }]);
    
    $stateProvider.state('index', {
        abstract: true,
        url: "/index",
        views: {
            '': {
                templateUrl: "views/content.html" // a base template to have sections replaced via ui-view elements
            }
        },
        ...
    });
    
    $ocLazyLoadProvider.config({
        loadedModules: ['futureStates'],
        modules: [
            {
                name: 'module1',
                files: ['module1/module.js']
            },
            {
                name: 'module2',
                files: ['module2/module.js']
            }
        ]
    });
    
    function ocLazyLoadStateFactory($q, $ocLazyLoad, futureState) {
        var deferred = $q.defer();
        // this loads the module set in the future state
        $ocLazyLoad.load(futureState.module).then(function () {
            deferred.resolve();
        }, function (error) {
            deferred.reject(error);
        });
        return deferred.promise;
    }
    $futureStateProvider.stateFactory('ocLazyLoad', ['$q', '$ocLazyLoad', 'futureState', ocLazyLoadStateFactory]);
    
    $futureStateProvider.futureState({
        'stateName': 'index.module1', // the state name
        'urlPrefix': '/index/module1', // the url to the state
        'module': 'module1', // the name of the module, configured in ocLazyLoad above
        'type': 'ocLazyLoad' // the future state factory to use.
    });
    $futureStateProvider.futureState({
        'stateName': 'index.module2',
        'urlPrefix': '/index/module2',
        'module': 'module2',
        'type': 'ocLazyLoad'
    });
    
    $futureStateProvider.addResolve(['$http', function ($http) {
        return $http({method: 'GET', url: '/url'}).then(function (states) {
            $futureStateProvider.futureState({
                'stateName': 'index.module2',
                'urlPrefix': '/index/module2',
                'module': 'module2',
                'type': 'ocLazyLoad'
            });
        });
    }]);
    
    $stateProvider.state('index.module1', {
        url: "/module1",
        abstract: true,
        resolve: {
            loadFiles: ['$ocLazyLoad', function($ocLazyLoad){
                return  return $ocLazyLoad.load(['list of all your required files']);
            }]
        }
    })
    $stateProvider.state('index.module1.sub1', {
        url: "/sub1",
        views: {
           // override your ui-views in here. this one overrides the view named 'main-content' from the 'index' state
           'main-content@index': {
                templateUrl: "module1/views/sub1.html"
            }
        }
    })