動的なモジュールセットを使用したAngularJSアプリの開発


81

ユーザーが(事前定義された100以上のウィジェットのセットから選択して)ウィジェットを配置(ドラッグ/ドロップ)できる複雑なレイアウトのアプリケーションがあります。すべてのウィジェットは、データのセットを表示するカスタム実装です(REST呼び出しを使用してフェッチされます)。特定の方法で。たくさんのブログ投稿、stackoverflowの質問、公式のAngularJSドキュメントを読みましたが、そこでの要件を処理するためにアプリケーションをどのように設計すればよいかわかりません。デモアプリを見ると、単一のモジュール(ng-app)があり、それを.jsファイルで構築すると、依存モジュールが依存関係として宣言されますが、ウィジェットのセットが大きいため、すべてを説明することはお勧めできません。そこ。次の質問についての提案が必要です。

  • アプリとウィジェットをどのように設計する必要がありますか?個別のAngularJSモジュールを用意する必要がありますか、それとも各ウィジェットをメインモジュールへのディレクティブにする必要がありますか?
  • ウィジェットをディレクティブとして設計する場合、ディレクティブ内で依存関係を定義する方法はありますか?つまり、私のディレクティブはその実装でng-calenderを使用していると言いますか?
  • 各ウィジェットを個別のモジュールとして設計する場合、ウィジェットモジュールを依存関係としてメインモジュールに動的に追加する方法はありますか?
  • コントローラーをどのように設計すればよいですか?ウィジェットごとに1つのコントローラーが必要ですか?
  • ビューに同じタイプのウィジェットが複数ある場合、状態(スコープ)をどのように分離する必要がありますか?
  • AngularJSで再利用可能なウィジェットを設計するためのベストプラクティスはありますか?

編集

役立つ参考資料:


1
私の最初の考えは、ng-includeを介してレンダリングされた個別のHTML / JSファイルのペアとして各ウィジェットを作成することでした。秘訣は、必要なコントローラーJSファイルのみをロードすることです。
Shmiddty 2013年

ng-includeを含むhtmlを動的に追加できますか?
エイドリアンミテフ2013年

1
私はそう信じています。メインコントローラーには、アクティブなウィジェットを定義するコレクションがあり、コレクションのマークアップには、ウィジェットのビューHTMLsrcを指すプロパティにバインドされたng-includeがあります。私はこれがうまくいくとかなり確信していますが、私は実際にこのようなことをしていません。
Shmiddty 2013年

回答:


61

これらは単なる一般的なアドバイスです。

アプリとウィジェットをどのように設計する必要がありますか?個別のAngularJSモジュールを用意する必要がありますか、それとも各ウィジェットをメインモジュールへのディレクティブにする必要がありますか?

あなたは何百ものウィジェットについて話しているので、それらをいくつかのモジュールに分割するのは自然なことのようです。一部のウィジェットには、他のウィジェットよりも多くの共通点がある場合があります。非常に一般的で他のプロジェクトに適合するものもあれば、より具体的なものもあります。

ウィジェットをディレクティブとして設計する場合、ディレクティブ内で依存関係を定義する方法はありますか?つまり、私のディレクティブはその実装でng-calenderを使用していると言いますか?

他のモジュールへの依存関係は、モジュールレベルで行われているが、モジュールがあれば問題はありませんAモジュールに依存Bし、両方ABモジュールに依存しますC。ディレクティブは、Angularでウィジェットを作成するための自然な選択です。ディレクティブが別のディレクティブに依存している場合は、同じモジュールでそれらを定義するか、モジュラーレベルで依存関係を作成します。

各ウィジェットを個別のモジュールとして設計する場合、ウィジェットモジュールを依存関係としてメインモジュールに動的に追加する方法はありますか?

なぜあなたがこれをしたいかわからないし、どうやってやるのかわからない。ディレクティブとサービスは、Angularで使用される前に初期化されません。ディレクティブ(ウィジェット)の膨大なライブラリがあり、おそらくそれらの一部を使用するが、すべてではないことがわかっている場合、アプリケーションが初期化されたときにどのディレクティブが使用されるかわからない場合は、実際に「遅延」することができます。モジュールがロードされた後、ディレクティブをロードします。ここに例を作成しました

利点は、コードがたくさんある場合でも、必要になる前にスクリプトをロードする必要がないため、アプリケーションを高速にロードできることです。欠点は、新しいディレクティブが最初にロードされるときにかなりの遅延が発生する可能性があることです。

コントローラーをどのように設計すればよいですか?ウィジェットごとに1つのコントローラーが必要ですか?

ウィジェットにはおそらく独自のコントローラーが必要です。コントローラは一般的に小さくする必要があります。大きくなった場合は、サービスにより適した機能があるかどうかを検討できます。

ビューに同じタイプのウィジェットが複数ある場合、状態(スコープ)をどのように分離する必要がありますか?

スコープ変数を必要とするウィジェットは、間違いなく独自の分離されたスコープを持つ必要があります(scope:{ ... }ディレクティブ構成内)。

AngularJSで再利用可能なウィジェットを設計するためのベストプラクティスはありますか?

スコープを分離し、依存関係を必要最小限に抑えます。Angularのベストプラクティスに関するMiskoのビデオを参照してください

Brian Fordは、Angularで巨大なアプリケーションを作成することについての記事も書いています。


素晴らしい答えをありがとう!
エイドリアンミテフ2013年

17

この質問も私にとって非常に重要です。AngularJSホームページにはいくつかの例があります(ウィジェットと呼ぶことができます)ので、ソースコードを調べて、ウィジェットがどのように分離されているかを確認しました。

まず、「ng-app」属性を宣言することはありません。彼らは使用します

function bootstrap() {
      if (window.prettyPrint && window.$ && $.fn.popover && angular.bootstrap &&
          hasModule('ngLocal.sk') && hasModule('ngLocal.us') && hasModule('homepage') && hasModule('ngResource')) {
            $(function(){
              angular.bootstrap(document, ['homepage', 'ngLocal.us']);
            });
      }
    }

すべてが正しくロードされていることを確認します。いい考えですが、彼らがng-app属性をあなたに押し付けすぎて、それ自体を使用しないのは奇妙なことです。とにかくここに彼らがアプリでロードするホームページモジュールがあります-http ://angularjs.org/js/homepage.js

appRunと呼ばれるディレクティブがあります

  .directive('appRun', function(fetchCode, $templateCache, $browser) {
    return {
      terminal: true,
      link: function(scope, element, attrs) {
        var modules = [];

        modules.push(function($provide, $locationProvider) {
          $provide.value('$templateCache', {
            get: function(key) {
              var value = $templateCache.get(key);
              if (value) {
                value = value.replace(/\#\//mg, '/');
              }
              return value;
            }
          });
          $provide.value('$anchorScroll', angular.noop);
          $provide.value('$browser', $browser);
          $locationProvider.html5Mode(true);
          $locationProvider.hashPrefix('!');
        });
        if (attrs.module) {
          modules.push(attrs.module);
        }

        element.html(fetchCode(attrs.appRun));
        element.bind('click', function(event) {
          if (event.target.attributes.getNamedItem('ng-click')) {
            event.preventDefault();
          }
        });
        angular.bootstrap(element, modules);
      }
    };
  })

例としてToDoリストを使用します。HTMLの場合、

<div app-run="todo.html" class="well"></div>

そしてページの下部に彼らが持っている

<script type="text/ng-template" id="todo.html">
  <h2>Todo</h2>
  <div ng-controller="TodoCtrl">
    <span>{{remaining()}} of {{todos.length}} remaining</span>
    [ <a href="" ng-click="archive()">archive</a> ]
    <ul class="unstyled">
      <li ng-repeat="todo in todos">
        <input type="checkbox" ng-model="todo.done">
        <span class="done-{{todo.done}}">{{todo.text}}</span>
      </li>
    </ul>
    <form ng-submit="addTodo()">
      <input type="text" ng-model="todoText"  size="30"
             placeholder="add new todo here">
      <input class="btn-primary" type="submit" value="add">
    </form>
  </div>
</script>

彼らも持ってる

<style type="text/css" id="todo.css"> //style stuff here </style>
<script id="todo.js"> //controller stuff here </script>

コードが使用されますが、これらのスクリプトのid属性は、アプリの実行には重要ではありません。これは、アプリの左側に表示されるソースコード用です。

基本的に、関数fetchCodeを使用するappRunというディレクティブがあります。

  .factory('fetchCode', function(indent) {
    return function get(id, spaces) {
      return indent(angular.element(document.getElementById(id)).html(), spaces);
    }
  })

コードをフェッチします。次に、angular.bootstrap()を使用して新しいアプリケーションを作成します。また、アプリの実行を通じてモジュールをロードすることもできます。JavaScriptプロジェクトの例は次のように初期化されます

<div app-run="project.html" module="project" class="well"></div>

うまくいけば、これが役立つでしょう。「最良の」手法が何であるかはまだわかりませんが、AngularJSホームページは、例/ウィジェットごとに完全に別個の角度アプリケーション(ng-app)を使用しているように見えます。私は同じことをするつもりだと思いますが、fetchCode関数を変更してAJAXで何かを取得する点が異なります。


3
このために+1。:私はここに同様の質問私はあなたと助けることができるかもしれないと思う持ってstackoverflow.com/questions/17557088/...
ダン・観世

あなたは私を正しい方向に向けました。hasModule関数に興味がある人のために、ここに実装を残します:function hasModule(name) { try { angular.module(name); } catch(err) { return false; } return true; }
Asier Paz
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.