342

私は最新のプロジェクトでAngularJSを使用しています。ドキュメントとチュートリアルでは、すべてのモデルデータがコントローラースコープに配置されます。私は、それがコントローラーで利用可能であり、したがって対応するビュー内にある必要があることを理解しています。

しかし、私はモデルが実際にそこに実装されるべきだとは思いません。たとえば、複雑でプライベート属性がある場合があります。さらに、別のコンテキスト/アプリで再利用したい場合があります。すべてをコントローラーに入れると、MVCパターンが完全に壊れます。

同じことがどのモデルの振る舞いにも当てはまります。DCIアーキテクチャを使用し、動作をデータモデルから分離する場合、動作を保持するために追加のオブジェクトを導入する必要があります。これは、役割とコンテキストを導入することによって行われます。

DCI == D ata C ollaboration Interaction

もちろん、モデルのデータと動作は、プレーンなjavascriptオブジェクトまたは任意の「クラス」パターンを使用して実装できます。しかし、それを行うためのAngularJSの方法は何でしょうか?サービスを使用していますか?

したがって、この質問に帰着します:

AngularJSのベストプラクティスに従って、コントローラーから切り離されたモデルをどのように実装しますか?

4

8 に答える 8

155

複数のコントローラーで使用できるものが必要な場合は、サービスを使用する必要があります。簡単な工夫の例を次に示します。

myApp.factory('ListService', function() {
  var ListService = {};
  var list = [];
  ListService.getItem = function(index) { return list[index]; }
  ListService.addItem = function(item) { list.push(item); }
  ListService.removeItem = function(item) { list.splice(list.indexOf(item), 1) }
  ListService.size = function() { return list.length; }

  return ListService;
});

function Ctrl1($scope, ListService) {
  //Can add/remove/get items from shared list
}

function Ctrl2($scope, ListService) {
  //Can add/remove/get items from shared list
}
于 2012-06-20T13:31:35.773 に答える
81

私は現在、このパターンを試しています。これは、DCIではありませんが、従来のサービス/モデルの分離(Webサービス(別名モデルCRUD)と通信するためのサービス、およびオブジェクトのプロパティとメソッドを定義するモデル)を提供します。

このパターンは、モデルオブジェクトが独自のプロパティで動作するメソッドを必要とする場合にのみ使用することに注意してください。おそらく、どこでも使用します(改善されたゲッター/セッターなど)。私はすべてのサービスに対してこれを体系的に行うことを推奨していません。

編集:私はこのパターンが「Angularモデルはプレーンな古いjavascriptオブジェクトです」というマントラに反すると思っていましたが、今ではこのパターンは完全に問題ないようです。

編集(2):さらに明確にするために、私はModelクラスを単純なゲッター/セッターを因数分解するためにのみ使用します(例:ビューテンプレートで使用されます)。大規模なビジネスロジックの場合、モデルを「認識」しているが、それらから分離されており、ビジネスロジックのみを含む個別のサービスを使用することをお勧めします。必要に応じて、これを「ビジネスエキスパート」サービスレイヤーと呼びます

service / ElementServices.js(要素が宣言にどのように挿入されているかに注意してください)

MyApp.service('ElementServices', function($http, $q, Element)
{
    this.getById = function(id)
    {
        return $http.get('/element/' + id).then(
            function(response)
            {
                //this is where the Element model is used
                return new Element(response.data);
            },
            function(response)
            {
                return $q.reject(response.data.error);
            }
        );
    };
    ... other CRUD methods
}

model / Element.js(angularjs Factoryを使用、オブジェクト作成用に作成)

MyApp.factory('Element', function()
{
    var Element = function(data) {
        //set defaults properties and functions
        angular.extend(this, {
            id:null,
            collection1:[],
            collection2:[],
            status:'NEW',
            //... other properties

            //dummy isNew function that would work on two properties to harden code
            isNew:function(){
                return (this.status=='NEW' || this.id == null);
            }
        });
        angular.extend(this, data);
    };
    return Element;
});
于 2013-02-13T11:22:24.877 に答える
29

Angularjsのドキュメントには次のように明記されています。

他の多くのフレームワークとは異なり、Angularはモデルに制限や要件を設けていません。継承するクラスや、モデルにアクセスまたは変更するための特別なアクセサメソッドはありません。モデルは、プリミティブ、オブジェクトハッシュ、または完全なオブジェクトタイプにすることができます。つまり、モデルはプレーンなJavaScriptオブジェクトです。

—AngularJS開発者ガイド-V1.5の概念-モデル

つまり、モデルを宣言する方法はあなた次第です。これは単純なJavascriptオブジェクトです。

Angular Servicesは、たとえば、アプリケーション全体でグローバル状態を維持するために使用できるシングルトンオブジェクトのように動作することを目的としていたため、個人的には使用しません。

于 2013-05-30T10:30:20.840 に答える
8

DCIはパラダイムであるため、言語がDCIをサポートするかどうかにかかわらず、angularJSでそれを行う方法はありません。JSは、ソース変換を使用する場合はDCIをかなり適切にサポートしますが、使用しない場合はいくつかの欠点があります。繰り返しになりますが、DCIは、C#クラスが持っていると言うよりも依存性注入とは関係がなく、間違いなくサービスでもありません。したがって、angulusJSを使用してDCIを実行する最良の方法は、DCIをJSの方法で実行することです。これは、最初にDCIが定式化される方法にかなり近いものです。ソース変換を行わない限り、ロールメソッドはコンテキスト外でもオブジェクトの一部になるため、完全に行うことはできませんが、これは通常、メソッドインジェクションベースのDCIの問題です。fullOO.infoを見るとDCIの信頼できるサイトでは、メソッドインジェクションも使用するrubyの実装を確認するか、DCIの詳細についてここを参照することができます。これは主にRUbyの例を使用していますが、DCIのものはそれに依存しません。DCIの鍵の1つは、システムが行うことをシステムが何であるかから分離することです。したがって、データオブジェクトはかなり馬鹿げていますが、コンテキストロールメソッドのロールにバインドされると、特定の動作が利用可能になります。ロールは単なる識別子であり、その識別子を介してオブジェクトにアクセスする場合は、ロールメソッドを使用できます。ロールオブジェクト/クラスはありません。メソッドインジェクションでは、ロールメソッドのスコーピングは説明されているとおりではありませんが、厳密です。JSのコンテキストの例は次のとおりです。

function transfer(source,destination){
   source.transfer = function(amount){
        source.withdraw(amount);
        source.log("withdrew " + amount);
        destination.receive(amount);
   };
   destination.receive = function(amount){
      destination.deposit(amount);
      destination.log("deposited " + amount);
   };
   this.transfer = function(amount){
    source.transfer(amount);
   };
}
于 2012-06-20T05:56:11.163 に答える
8

AngularJSのモデルに関するこの記事は、次のことに役立つ可能性があります。

http://joelhooks.com/blog/2013/04/24/modeling-data-and-state-in-your-angularjs-application/

于 2013-09-12T16:03:53.417 に答える
5

他のポスターで述べられているように、Angularはモデリング用のすぐに使える基本クラスを提供していませんが、いくつかの機能を便利に提供できます。

  1. RESTfulAPIと対話して新しいオブジェクトを作成するためのメソッド
  2. モデル間の関係の確立
  3. バックエンドに永続化する前にデータを検証します。リアルタイムエラーの表示にも役立ちます
  4. 無駄なHTTPリクエストを行わないようにするためのキャッシュと遅延読み込み
  5. ステートマシンフック(保存、更新、作成、新規などの前後)

これらすべてをうまく実行するライブラリの1つは、ngActiveResource(https://github.com/FacultyCreative/ngActiveResource)です。完全な開示-私はこのライブラリを作成しました-そして私はいくつかのエンタープライズ規模のアプリケーションを構築する際にそれをうまく使用しました。十分にテストされており、Rails開発者に馴染みのあるAPIを提供します。

私のチームと私はこのライブラリを積極的に開発し続けており、より多くのAngular開発者がこのライブラリに貢献してバトルテストを行うことを望んでいます。

于 2014-01-11T07:44:32.223 に答える
5

古い質問ですが、Angular 2.0の新しい方向性を考えると、このトピックはこれまで以上に関連性が高いと思います。ベストプラクティスは、特定のフレームワークへの依存関係をできるだけ少なくしてコードを作成することです。直接的な価値を付加するフレームワーク固有の部分のみを使用してください。

現在、Angularサービスは次世代のAngularを実現する数少ない概念の1つであるように思われるため、すべてのロジックをサービスに移行するという一般的なガイドラインに従うのが賢明でしょう。ただし、Angularサービスに直接依存していなくても、分離モデルを作成できると私は主張します。必要な依存関係と責任のみを持つ自己完結型のオブジェクトを作成することは、おそらく進むべき道です。また、自動テストを行う際の作業が大幅に楽になります。最近の単一責任は話題の仕事ですが、それは非常に理にかなっています!

これは、オブジェクトモデルをdomから切り離すのに適していると私が考えるパターンの例です。

http://www.syntaxsuccess.com/viewarticle/548ebac8ecdac75c8a09d58e

重要な目標は、ビューからと同じように単体テストからも簡単に使用できるようにコードを構造化することです。あなたがそれを達成した場合、あなたは現実的で有用なテストを書くのに適した立場にあります。

于 2014-12-19T16:54:22.457 に答える
4

私はこのブログ投稿でその正確な問題に取り組んでみました。

基本的に、データモデリングに最適な場所は、サービスと工場です。ただし、データを取得する方法と必要な動作の複雑さに応じて、実装を行うにはさまざまな方法があります。Angularには現在、標準的な方法やベストプラクティスがありません。

この投稿では、$ http$ resource、およびRestangularを使用した3つのアプローチについて説明しています。

JobモデルのカスタムgetResult()メソッドを使用した、それぞれのサンプルコードを次に示します。

Restangular(easy peasy):

angular.module('job.models', [])
  .service('Job', ['Restangular', function(Restangular) {
    var Job = Restangular.service('jobs');

    Restangular.extendModel('jobs', function(model) {
      model.getResult = function() {
        if (this.status == 'complete') {
          if (this.passed === null) return "Finished";
          else if (this.passed === true) return "Pass";
          else if (this.passed === false) return "Fail";
        }
        else return "Running";
      };

      return model;
    });

    return Job;
  }]);

$ resource(少し複雑):

angular.module('job.models', [])
    .factory('Job', ['$resource', function($resource) {
        var Job = $resource('/api/jobs/:jobId', { full: 'true', jobId: '@id' }, {
            query: {
                method: 'GET',
                isArray: false,
                transformResponse: function(data, header) {
                    var wrapped = angular.fromJson(data);
                    angular.forEach(wrapped.items, function(item, idx) {
                        wrapped.items[idx] = new Job(item);
                    });
                    return wrapped;
                }
            }
        });

        Job.prototype.getResult = function() {
            if (this.status == 'complete') {
                if (this.passed === null) return "Finished";
                else if (this.passed === true) return "Pass";
                else if (this.passed === false) return "Fail";
            }
            else return "Running";
        };

        return Job;
    }]);

$ http(ハードコア):

angular.module('job.models', [])
    .service('JobManager', ['$http', 'Job', function($http, Job) {
        return {
            getAll: function(limit) {
                var params = {"limit": limit, "full": 'true'};
                return $http.get('/api/jobs', {params: params})
                  .then(function(response) {
                    var data = response.data;
                    var jobs = [];
                    for (var i = 0; i < data.objects.length; i ++) {
                        jobs.push(new Job(data.objects[i]));
                    }
                    return jobs;
                });
            }
        };
    }])
    .factory('Job', function() {
        function Job(data) {
            for (attr in data) {
                if (data.hasOwnProperty(attr))
                    this[attr] = data[attr];
            }
        }

        Job.prototype.getResult = function() {
            if (this.status == 'complete') {
                if (this.passed === null) return "Finished";
                else if (this.passed === true) return "Pass";
                else if (this.passed === false) return "Fail";
            }
            else return "Running";
        };

        return Job;
    });

ブログ投稿自体では、各アプローチを使用する理由の背後にある理由と、コントローラーでモデルを使用する方法のコード例について詳しく説明しています。

AngularJSデータモデル:$ http VS $ resource VS Restangular

Angular 2.0は、全員が同じページにいるデータモデリングに対してより堅牢なソリューションを提供する可能性があります。

于 2014-07-17T19:34:33.727 に答える