$resource 如何适应 Angular 风格指南?

How would $resource fit into the Angular Style Guide?

我们很快将根据 Angular Style Guide. The guide itself is great (and can be found slightly modified all over the interwebs), but no one mentions how $resource fits into a factory, or any reasons why it might have been left out. One guide says to use $resource over $http where you can 重构我们的代码,但不会将其添加到他们的工厂样式中:/。

我记得在很多地方读到 $resource 更好,这就是我开始使用它的原因,但现在我忘记了原因并想知道这是否仍然正确 - 特别是考虑到资源对象在底部这个post。有一些意见 (Papas own, and again) 认为 $resource(不是?)很棒,但这是我正在重新检查的另一个问题。

那么,假设我们要使用 $resource 并给出下面的示例代码,$resource 适合哪里,以便它符合指南中样式背后的推理? 另外,如果你的答案是 "It doesn't. The style [subtly] recommends $http because bla, bla and bla.",那也很有用。

(function() {
    'use strict';

    angular
        .module('myModule')
        .factory('oneService', oneService);

    predicateService.$inject = ['twoService', 'anotherService'];

    /* @ngInject */
    function oneService(twoService, anotherService) {
        var service = {
            doSomething: doSomething,
            etc: etc
        };

        // pos 1 (it really only works here but can be LONG)
        // var fancyResource = $resource('/path/to/thing', '...');

        // Ideally, this should be kept close to the top, right?
        return service;

        // pos 2 (here or below ////// is cleaner, but doesn't work)
        // var fancyResource = $resource('/path/to/thing', '...');

        ////////////////

        function doSomething() {}

        // rest of functions here etc...
    }
})();

现在,我们唯一使用 $resource 的地方(也许这也是不正确的)是在像 doSomething() 这样的方法中。在过去的不同点,甚至在我们今天的代码中的不同地方,fancyResource 由服务生成 public 并直接从控制器使用:oneService.fancyResource.get()。我认为这可能是 $resource 的预期用途,但我不确定了。

此外,考虑到一个服务可能非常大(不要在意其中一些 should/could 被分解成多个资源这一事实;让我们假设一个可能有这种大小的资源对象并且需要许多动词):

var userResource = $resource(baseApiPath + 'users', {}, {
    get: {
        method: 'GET',
        headers: utilityService.getHeaders('sampling'),
        isArray: true,
        transformResponse: function(response){
            response = JSON.parse(response);
            if(response.result){
                return response.result.users;
            }
            return response;
        }
    },
    getUserDetails: {
        method: 'GET',
        url: baseApiPath+'users/:userId',
        params: {
            userId: '@userId'
        },
        headers: utilityService.getHeaders('sampling'),
        transformResponse: function(response){
            response = JSON.parse(response);
            if(response.result){
                return response.result.user;
            }
            return response;
        }
    },
    getUserByRole: {
        method: 'GET',
        url: baseApiPath+'users/roles/:roleId',
        params: {
            roleId: '@roleId'
        },
        headers: utilityService.getHeaders('sampling'),
    },
    getLoggedInUserData: {
        method: 'GET',
        url: baseApiPath + 'users/userData',
        headers: utilityService.getHeaders('sampling'),
    },
    getGrantedAuth: {
        method: 'GET',
        url: baseApiPath+'users/applicationPermissions/userId/:userId/:applicationId/',
        params: {
            applicationId: '@applicationId',
            userId: '@userId'
        },
        headers: utilityService.getHeaders('sampling'),
    }
});

所以,我想我已经根据一些想法找到了答案。

首先,我现在意识到像这样使用 $resource 是完全不正确的,原因有二。首先是我正在创建额外的动作,这些动作需要它们自己的独特路径。 $resource 的全部意义在于使 GETPUTPOSTDELETE 单个 REST 资源上更容易.我基本上是在整合我的资源,因为它们看起来是统一的。例如,/users/users/roles/:roleId 应该是两个不同的资源(并且可能放入两个不同的服务以保持单一责任风格)。

我错误使用 $resource 的第二种方式实际上是因为我并没有真正使用它提供给我的 querysavedelete 方法。我会为我想做的任何事情创建另一个自定义操作。有时这还包括一个独特的 URL,如 /users/:userId/delete,这是因为 API 并不总是 REST API。 $resource 专为符合 REST 标准的 API 设计。因为它包装了$http并且可以给它传递参数,所以很容易落入这个陷阱。 $resource 不打算成为多个 $http 用途的​​配置。

所以,现在已经解决了这个问题,下面是我建议如何将 $resource 包含到工厂中,并且仍然遵循样式指南。

首先,$resource 只能与真正的 REST 一起使用 API。一个只有 have/need 一条路径,并且 only/mostly HTTP 方法用于与之交互。此外,由于工厂旨在表示和管理一种 'thing',与 'thing API' 交互,每个服务实际上应该只有一个 $resource。扩展示例,将有一个用户服务和一个角色服务;每个都有一个$resource。然后可能会有另一个 userRoleService 同时使用它们,并且实际上不自己做任何 $resource 的事情。反正就是这样。

在这种情况下,$resource 配置实际上比我最初发布的要短得多。由于它更小,我们可以将它更像是一个变量声明,并将其放在我们创建的服务对象之上。

(function() {
    'use strict';

    angular
        .module('myModule')
        .factory('oneService', oneService);

    predicateService.$inject = ['anotherService', '$resource'];

    /* @ngInject */
    function oneService(anotherService, $resource) {

        // this resource is unlikely to get longer than this
        var userResource = $resource('http://api.com/users/:userId', {
            userId: '@userId'
        });

        // and we're still able to see all bindables at the top
        var service = {
            doSomething: doSomething,
            etc: etc
        };
        return service;

        ////////////////

        function doSomething() {
            // and in a service method, we can use the resource like this,
            userResource.query().$promise
            .then(function(response){...})
        }

        function doSomethingElse() {
            // or we could use the $resource in a way that would make
            // chainable with another .then() in the calling method.
            var load = userResource.query();

            // do some internal stuff when we get the response
            load.$promise
            .then(function(response){...});

            // then return the $resource object, or load.$promise
            // so that another .then can be chained afterwards.
            return load;
        }

        // rest of functions here etc...
    }
})();

无论如何,这就是我想出的答案。我希望这对那些来这里寻找我正在寻找的东西(但不容易找到)的人有所帮助。