如何延长一个AngularJS资源($资源)的构造?(How can I extend the co

2019-09-02 07:56发布

我有一个模型,使用定义$resource ,是我成功加载。

每个加载的实例是,如许,我所定义的类的实例。

(下面的例子是从角文档。在这里面, User.get导致一个对象,它是一个instanceof User )。

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

然而,可以想象每个用户过来这样的线:

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

我定义了一个Preference的工厂,增加了宝贵的方法来Preference对象。 但是,当用户的负荷,这些preferences并不Preference S,自然。

我尝试这样的:

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
}

但它没有任何效果,从来没有记录任何东西。

我怎样才能让我的每一个项目User s'的preferences阵列的一个实例Preference

Answer 1:

$资源是一个简单的实现,并在这样的事情缺乏。

User.prototype.constructor不会做任何事情; 角不会尝试像它的面向对象,不像其他库。 这只是JavaScript的。

..但幸运的是,你有承诺和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);


Answer 2:

您可以通过覆盖内置资源行动来改变请求和响应做到这一点(见transformRequest和transformResponse的文档 。):

var m = angular.module('my-app.resources');
m.factory('User', [
          '$resource',
  function($resource) {

    function transformUserFromServer(user) {
      // Pass Preference directly to map since, in your example, it takes a JSON preference as an argument
      user.preferences = _.map(user.preferences, Preference);
      return user;
    }

    function transformUserForServer(user) {
      // Make a copy so that you don't make your existing object invalid
      // E.g., changes here may invalidate your model for its form, 
      //  resulting in flashes of error messages while the request is 
      //  running and before you transfer to a new page
      var copy = angular.copy(user);
      copy.preferences = _.map(user.preferences, function(pref) {
        // This may be unnecessary in your case, if your Preference model is acceptable in JSON format for your server
        return {
          id: pref.id,
          title: pref.title,
          value: pref.value
        };
      });

      return copy;
    }

    function transformUsersFromServer(users) {
      return _.map(users, transformUserFromServer);
    }

    return $resource('/user/:userId', {
        userId: '@id'
      }, {
        get: {
          method: 'GET',
          transformRequest: [
            angular.fromJson,
            transformUserFromServer
          ]
        },
        query: {
          method: 'GET',
          isArray: true,
          transformRequest: [
            angular.fromJson,
            transformUsersFromServer
          ]
        },
        save: {
          method: 'POST',
          // This may be unnecessary in your case, if your Preference model is acceptable in JSON format for your server
          transformRequest: [
            transformUserForServer,
            angular.toJson
          ],
          // But you'll probably still want to transform the response
          transformResponse: [
            angular.fromJson,
            transformUserFromServer
          ]
        },
        // update is not a built-in $resource method, but we use it so that our URLs are more RESTful
        update: {
          method: 'PUT',
          // Same comments above apply in the update case.
          transformRequest: [
            transformUserForServer,
            angular.toJson
          ],
          transformResponse: [
            angular.fromJson,
            transformUserFromServer
          ]
        }
      }
    );
  };
]);


Answer 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;
    },

    // ...
});

最引人注目的部分是:

  • 自定义实体类,即能构建/填充自己从一个资源实例(可能与深拷贝能力,例如在报价位置)开始
  • 自定义库类,它封装了资源。 这并不返回经典的异步资源的答案,而是返回自定义实体实例,后来它填补了这个与资源只是加载。


Answer 4:

试图修改原型对象你期望什么,无论如何不会做的constructor属性,请看看很不错的职位在这里 。

要真正了解是怎么回事,应该看看源代码中的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并返回你想建立取其自定义类。



Answer 5:

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


文章来源: How can I extend the constructor of an AngularJS resource ($resource)?