2016-01-27 15 views
16

私は、アプリケーションコンテンツをサーバから動的に読み込むためのソリューションを探しています。サーバからHTMLとコントローラをロードし、動的状態を作成するUI - ルータ

私のシナリオ:

は、我々は2人のユーザー(AとB)を持っているとしましょう、今の私の目標は、から私のアプリにユーザーがログインとなり、shoppingListと電卓を言うことができますように、私のアプリは、異なるモジュールで構成されていデータベース私はユーザー権利を取得し、彼が持っている権利に応じて、私はhtmlとctrlに必要な状態を作成しながら、サーバーからロジック部分のビューとコントローラファイルのHTMLをロードします。だから基本的に私のアプリケーションは、ログインの一貫性が非常に小さく、他のすべてがUserrightsに応じてサーバーから引っ張ってきている。

私は何を使用します。

  1. コルドバ
  2. AngularJs
  3. イオン枠組み

私はそれがすべて動的にする必要がある理由:

1)ANを持っているpossiblityログインロジックだけを含んでいるアプリだから、バグを修正したり、モジュールを追加したりするときに、ファイルをサーバに追加するだけで済むユーザーにその権利を与えてください。アプリを更新する必要はありません。

2)ユーザーには必要な機能のみがあり、1つのモジュールにしか権利がない場合は、すべてを持つ必要はありません。

3)このアプリケーションは非常に大きくなります。つまり、すべてのモジュールが独自のhtmlとコントローラで5〜10個の状態を持つことを意味します。現在、50種類のモジュールが計画されているため、数学を行うことができます。

私はいくつかのインスピレーションを得るためにこれを見た:

AngularJS, ocLazyLoad & loading dynamic States

私がこれまで試したどのような:

私が唯一の1つのhttpリクエストを持っているので、全体のモジュールが含まれている1つのHTMLファイルを作成:

ユーザーが

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]); 

      } 
     } 
    }); 

コントローラーパート:

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]); 

      } 
     } 
    }); 

私はそれらを登録しようとするコントローラーロードした後:

$controllerProvider.register('SomeName', $templateCache.get('controllers/someController)); 

これが唯一の文字列であるため、機能していません...

.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-ルータパート:プロバイダの定義

これまで

//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をどのように使用するか分かりません。保存された文字列...

が状態を作成するには、仕事をしましたが、コントローラはので、私はすべてのビューを開くことができませんでしたフィットdidntの...

PS:私はまたrequire.jsとOCLazyLoadを見てだけでなく、この例dynamic controller example

更新:

私はHtmlをロードすることができたので、ControllerStateを作成してください。コントローラがまったく動作していないようで、エラーはありませんが、コントローラのロジックは何も表示されません。実行される。現在のところ、以前にダウンロードしたファイルからコントローラを登録する唯一の解決策は、より多くのハックで適切な解決策である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(); 

     } 
    } 
}]) 

すべてのヘルプは、[OK]を

+0

なぜすべてをロードせず、ユーザーのアクセスに基づいて未使用のメニュー項目を非表示にするのはなぜですか?それはテンプレートが多くのスペースを取っているようではありません。 – Matt2012

+0

この場合、アプリケーションは非常に大きくなるので、私はこのようにする必要があります。 – stackg91

+3

私はそういうわけではありません。もはや2000年代前半ではない。最近ではhtml5 + ajaxコールのすべてがファットクライアントです。私はすべてを置くだろう。クライアント上のすべてのモジュール50モジュール、特にhtml/css/javascriptは冗談です。モバイルでも。 – foreyez

答えて

4

を高く評価し、その者は、最初から始めましょう。

すべてのアプリケーションロジックは、サーバーに含まれ、REST、SOAPなどのAPIコールを介して提供される必要があります。これにより、UIに組み込まれているロジックの量が削減され、クライアントに対する負荷が軽減されます。これは、基本的に、クライアントアプリケーションをレンダリングエージェントにし、バックエンドAPIによって提供されるデータとロジックのモデルとビューのみを含むものです。

これは現代(または半近代)のデバイスでは問題ではありません。

すべてのレイアウトを一度に読み込まないようにしたい場合は、もちろんそれらを部分分割して、ユーザー特権に基づいてログイン後に読み込むことができます。こうすることで、メモリ内のデータの量を減らすことができます。ただし、改善は疑いの余地がありません。

+0

上記のコメントを参照してください。 – stackg91

+0

ロジックはまだデバイス上にあり、コントローラファイルをロードして登録するだけでサーバからプリロードされますコントローラとして – stackg91

+0

まあ、私の答えは同じままです。ただし、リリース日がローカルキャッシュの日付と異なる場合は、クライアントアプリケーションを単純なブラウザラッパーにして、html/jsファイルを再ダウンロードすることを検討する必要があります。 –

2

私はこれらのことを念頭に置いてアプリケーションを開発しました。ここに私の建築があります。

フォルダ構造:

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 ... 

:資本金ケースはフォルダを示しています。 ModuleAの横に、私の思うようなケースのLoginModuleがあります。あるいは、CommonModuleを使用することもできます。

メフは次のようになります。

<a href="/ModuleA/">Module A</a> <!--Note: index.jsp are indexed file 
           //for a directive --> 
<a href="/ModuleB/">Module B</a> 

これらのJSPページのそれぞれは、実際には独立した角度のアプリケーションです。次のコードを使用します。

ModuleA/index.jspを

<!-- Check User Permission Here also for Security 
    If permission does not have show Module Unavailable Kind of JSP. 
    Also do not send any JS files for this module. 
    If permission is there then use this following JSP 
--> 
<!DOCTYPE HTML> 
<html lang="en" data-ng-app="ModuleA"> 
    <head> 
     <title>Dynamic Rule Engine</title> 
     <%@ include file="scripts.jsp" %> 
     <%@ include file="templates.jsp" %> <!-- Can be cached it in 
               different way --> 
    </head> 
    <body> 
     <%@ include file="../common.jsp" %> 
     <div id="ngView" data-ng-view></div> 
     <%@ include file="../footer.jsp" %> 
    </body> 
</html> 

ModuleA/scripts.jsp

<%@ include file="../CommonModule/scripts.jsp" %> <!-- Include Common Things 
               Like Jquery Angular etc --> 
<scripts src="Controlers/ModlueAController1.js"></script> 
..... 

ModuleA/templates.jsp

<%@ include file="../CommonModule/templates.jsp" %> 
<!-- Include Common Templates for common directives --> 
<jsp:include page="../CommonModule/ng-include.jsp"><jsp:param name="src" value="ModuleA/Templates/template1.jsp" /></jsp:include> 
..... 

CommonModule/NG-include.jsp

<script type="text/ng-template" id="${param.src}"> 
    <jsp:include page="${param.src}" /> 
</script> 

しかし、このアプローチの主な問題は、ユーザがモジュールに変更されます場合は、ページが更新されますです。

EDIT: 次のように実際のモジュール減速を含んでModuleA.module.jsファイルがあります。

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() { 

    }); 
+0

allの共通index.jspページを作成して、異なるモジュール名。そして、そのモジュール名に依存して、サーバー側からアプリケーションを制御する 'data-ng-app =" <%= moduleName%> "' –

+0

これは私が探しているものに非常によく似ています。どのようにサーバーからロードした後、私のコントローラを登録することができます、私はHTMLを読み込んでテンプレートとして使用することができます、私は動的に状態を作成することができる、私はコントローラをロードすることができますが、文字列であり、eval( 'ctrlstring')でもすべてが正しく動作しているわけではありません。 – stackg91

3

状態をロードする方法をいくつか変更することを提案できますか?
ユーザーがアクセスできる状態でjsonを返すスクリプトを作成します。
Ex。
リソース/ルーティング-config.yourLangageユーザー=ユーザ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でこのサービスを使用してみましょう:すべてがスーパー柔軟かつ遅延ロードされます。このように

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); 
    }); 

}]); 

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。

私はすでに3つの異なるユーザーポータルを書いており、私が望むすべてのユーザーポータルに簡単に拡大することができます。

+0

これは非常に有望ですが、これを試してみると、私はすでにロードできるので、OcLazyLoad経由でこれを行う方法を見つけましたすべてのモジュールのコントローラとhtmlファイルは、それを月曜日に行って、それがどのように動いたかを教えてくれます – stackg91

1

私はあなたが求めていることをやっていると思います。私はこれを、UI-Router、ocLazyLoad、およびui-routerの将来の状態を使用して実現します。基本的に私たちのセットアップでは、同じコードベースに、ユーザーがアプリを開いたときに50以上のモジュールを持つことができます。そのアプリケーションで必要な基本ファイルを読み込むだけで起動します。次に、ユーザーが状態間を移動すると、アプリケーションは必要なファイルをそのパーツに必要なファイルとしてロードします。 (断片化されたコードの謝罪、私はコードベースから取り除かなければならなかったが、ソリューションに実際に関連するものだけを提供しようとした)。まず

、フォルダ構造

  • コアアプリ

config.jsの

  • モジュール1(/モジュール1)

module.js

controllers.js

  • モジュール2(/モジュール2)

module.js

controllers.js

など

config.jsの:

最初に行うのは基本状態を作成することです。これは抽象的な状態なので、ユーザーは実際にはそれを打つことはできません。

$stateProvider.state('index', { 
    abstract: true, 
    url: "/index", 
    views: { 
     '': { 
      templateUrl: "views/content.html" // a base template to have sections replaced via ui-view elements 
     } 
    }, 
    ... 
}); 

その後、我々はocLazyLoadにモジュールを設定します。これにより、モジュールをロードするようにocLazyLoadに指示するだけで、必要なすべてのファイルがロードされます(ただし、この例では1つのファイルですが、各モジュールのパスを変更することができます)。

$ocLazyLoadProvider.config({ 
    loadedModules: ['futureStates'], 
    modules: [ 
     { 
      name: 'module1', 
      files: ['module1/module.js'] 
     }, 
     { 
      name: 'module2', 
      files: ['module2/module.js'] 
     } 
    ] 
}); 

次に、(将来の状態によって)リクエストされたときに、ui-routerがモジュールをロードできるようにする関数を作成します。

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' 
     }); 
    }); 
}]); 

はその後、我々はモジュールを設定します。
モジュール1/module.js

$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 
     '[email protected]': { 
      templateUrl: "module1/views/sub1.html" 
     } 
    } 
}) 
関連する問題