15

を使用して定義されたモデルがあり、$resource正常にロードされています。

ロードされた各インスタンスは、約束どおり、私が定義したクラスのインスタンスです。

(以下の例は、Angular ドキュメントからのものです。その中で、User.getであるオブジェクトが生成されますinstanceof User。)

var User = $resource('/user/:userId', {userId:'@id'});

ただし、各ユーザーが次のように接続されていると想像してください。

{
  "username": "Bob",
  "preferences": [
    {
      "id": 1,
      "title": "foo",
      "value": false
    }
  ] 
}

オブジェクトPreferenceに価値のあるメソッドを追加するファクトリを定義しました。Preferenceしかし、 User が読み込まれるとき、それらは当然 s でpreferencesはありません。Preference

私はこれを試みました:

User.prototype.constructor = function(obj) {
  _.extend(this, obj);
  this.items = _.map(this.preferences, function(pref) {
    return new Preference(pref);
  });
  console.log('Our constructor ran'); // never logs anything
}

しかし、それは効果がなく、何も記録しません。

Users'preferences配列の各項目を のインスタンスにするにはどうすればよいPreferenceですか?

4

5 に答える 5

12

$resource は単純な実装であり、このような点が欠けています。

User.prototype.constructor何もしません。angular は、他のライブラリとは異なり、オブジェクト指向のように振る舞おうとしません。それはただのジャバスクリプトです。

..しかし幸いなことに、約束と JavaScript があります :-)。これを行う方法は次のとおりです。

function wrapPreferences(user) {
  user.preferences = _.map(user.preferences, function(p) {
    return new Preference(p);
  });
  return user;
}

var get = User.get;
User.get = function() {
  return get.apply(User, arguments).$then(wrapPreferences);
};
var $get = User.prototype.$get;
User.prototype.$get = function() {
  return $get.apply(this, arguments).$then(wrapPreferences);
};

これを、リソースのメソッドのいずれかをデコレートするメソッドに抽象化できます。これは、オブジェクト、メソッド名の配列、およびデコレータ関数を取ります。

function decorateResource(Resource, methodNames, decorator) {
  _.forEach(methodNames, function(methodName) {
    var method = Resource[methodName];
    Resource[methodName] = function() {
      return method.apply(Resource, arguments).$then(decorator);
    };
    var $method = Resource.prototype[methodName];
    Resource.prototype[methodName] = function() {
      return $method.apply(this, arguments).$then(decorator);
    };
  });
}
decorateResource(User, ['get', 'query'], wrapPreferences);
于 2013-05-09T02:48:13.327 に答える
3

私はあなたと同じ問題の解決策を探していました。私は次のアプローチを思いつきました。
この例は、ドメイン エンティティとして、ユーザーではなくオファーに基づいています。また、ここに全体の縮小版があることに注意してください。私の場合、いくつかのファイルにまたがっています。

ドメイン エンティティのカスタム クラス:

function Offer(resource) {
    // Class constructor function
    // ...
}

angular.extend(Offer.prototype, {
    // ...

    _init: function (resource) {
        this._initAsEmpty();

        if (typeof resource == 'undefined') {
            // no resource passed, leave empty
        }
        else {
            // resource passed, copy offer from that
            this.copyFromResource(resource);
        }
    },

    copyFromResource: function (resource) {
        angular.extend(this, resource);
        // possibly some more logic to copy deep references
    },

    // ...
});

クラシック角度カスタム リソース:

var offerResource = $resource(/* .. */);

サービス ファクトリによってコントローラに渡されるカスタム リポジトリ:

function OfferRepository() {  
    // ...
}

angular.extend(OfferRepository.prototype, {
    // ...

    getById: function (offerId, success, error) {

        var asyncResource = offerResource.get({
            offerId: offerId

        }, function (resource) {
            asyncOffer.copyFromResource(resource);

            (success || angular.noop)(asyncOffer);

        }, function (response) {
            (error || angular.noop)(response);

        });

        var asyncOffer = new offerModels.Offer(asyncResource);

        return asyncOffer;
    },

    // ...
});

最も注目すべき部分は次のとおりです。

  • リソース インスタンスから開始してそれ自体を構築/埋めることができるカスタム エンティティ クラス (おそらくディープ コピー機能を使用します。たとえば、オファーのポジション)
  • リソースをラップするカスタム リポジトリ クラス。これは従来の非同期リソースの回答を返すのではなく、代わりにカスタム エンティティ インスタンスを返し、後でそれを読み込んだばかりのリソースで埋めます。
于 2013-07-03T23:57:33.833 に答える
2

プロトタイプ オブジェクトのコンストラクター プロパティを変更しようとしても、期待どおりの結果が得られません。こちらの非常に優れた投稿をご覧ください

何が起こっているのかを本当に理解するには、モジュールのソース コードngResourceを調べる必要があります。そこでは多くのことが行われていますが、重要なのは、$resourceファクトリがプレーンな JavaScript 関数を返すことです (実際には他に何がありますか)。文書化されたパラメーターを使用してこの関数を呼び出すと、Resourceコンストラクター オブジェクトが返されます。このオブジェクトは、 でプライベートに定義されていresourceFactoryます。

覚えているかもしれませんが、AngularJS サービスはシングルトンです。つまり、呼び出す$resourceと毎回同じ関数が返されます (この場合はresourceFactory)。重要な点は、この関数が評価されるたびに新しいResourceコンストラクター オブジェクトが返されることです。つまり、これによってすべてのResourceインスタンスがグローバルに汚染されることを心配することなく、独自の関数を安全にプロトタイプ化できます。

$resourceすべてのインスタンスで使用できる独自のカスタム メソッドを定義しながら、元のファクトリと同じように使用できるサービスを次に示します。

angular.module('app').factory('ExtendedResourceFactory', ['$resource',
  function($resource) {                                                        
    function ExtendedResourceFactory() {
      var Resource = $resource.apply(this, arguments);

      Resource.prototype.myCustomFunction = function() {
        ...
      };

      return Resource;
    }

    return ExtendedResourceFactory;
  }
]);

内部myCustomFunctionでは、サーバーから返されたデータにアクセスできるため、作成しthis.preferencesたいカスタム クラスを使用して返すことができます。

于 2015-02-12T09:53:24.887 に答える
1

transformResponse仕事をします。例を考えてみましょう (Autolinker を使用して応答コンテンツをフォーマットしたかった)。

return $resource('posts/:postId', {
    postId: '@_id'
}, {
    get : {
        transformResponse : function(data) {
            var response = angular.fromJson( data );
            response.content = Autolinker.link(response.content);
            return response;
        }
    },
    update: {
        method: 'PUT'
} });
于 2015-01-10T22:38:36.740 に答える