9

I have a $resource whose API will always return some data that needs to be cleaned up before going into the presentation layer. Specifically, it's .NET returning Date objects in the lovely '/Date(...)/' format.

I don't want to have to write a callback every time I call .query() or .get(). Is there some way to extend the resource with a callback that gets called upon REST methods that update the instance's properties, or by adding some sort of $watch that gets fired when the date property changes? Basically something that will happen for every instance of this $resource.

angular.module('myAppServices', ['ngResource'])
    .factory('Participant', ['$resource', function ($resource) {
        var res = $resource('api/url/participants/:id', { id: '@id' });

        // This obviously doesn't work, but something kinda like this?
        res.prototype.$watch(this.FieldName, function(newVal, oldVal) {
            if (needsCleaning(newVal.fieldName) {
                this.FieldName = cleanupField(newVal);
            }
        };
    });
Scotty.NET
  • 12,533
  • 4
  • 42
  • 51
c0bra
  • 2,982
  • 23
  • 34
  • can u try this. `var participant = $resource('api/url/participants/:id', { id: '@id' }); var commonCallback = function(){ // inside this callback, participant is the populated model, // you can use participant object to do your formatting logic. // Then, if you would like to save the participant object do //participant.$save(); Please not the $ prefixed method names that becomes available to your resources instances. } res.query(commonCallback) or res.get({..}, commonCallback)` – Rajkamal Subramanian Apr 05 '13 at 20:11

3 Answers3

12

Ah-ha, I found a way around it and will leave it here. In version 1.1.2 they added support for passing all the $http.config options to a $resource. Naturally, the CDN I'm using doesn't have a recent enough version of angular-resource.js, but switching CDNs solved that.

I just used the transformResponse option to modify the data as it comes back.

angular.module('myAppServices', ['ngResource'])
    .factory('Participant', ['$resource', '$http', function ($resource, $http) {
        var res = $resource('api/url/participants/:id', { id: '@id' }, {
        save: {
            method: 'POST',
            transformResponse: $http.defaults.transformResponse.concat([
                function (data, headersGetter) {
                    data.FieldName = yourDateParsingFunction(data.FieldName);

                    return data;
                }
            ])
        }
    });

I'm just adding my transformer on to $httpProvider's transformResponse, which will do all the deserialization, etc.

c0bra
  • 2,982
  • 23
  • 34
8

An easy way to do this is to overwrite the existing $resource methods you want to do post-processing on with your own. See the code and comments below for an example.

angular.module('myAppServices', ['ngResource'])
    .factory('Participant', ['$resource', function ($resource) {
        var res = $resource('api/url/participants/:id', { id: '@id' }, {
            // create aliases for query and get to be used later
            _query: { method: 'GET', isArray: true },
            _get:   { method: 'GET' }
        });

        // redefine the query method
        res.query = function() {
            // call the original query method via the _query alias, chaining $then to facilitate
            // processing the data
            res._query.apply(null, arguments).$then(function(res) {
                var data = res.data;
                // do any processing you need to do with data here
                return data;
            });
        };

        // redefine the  method
        res.get = function() {
            // call the original get method via the _get alias, chaining $then to facilitate
            // processing the data
            res._get.apply(null, arguments).$then(function(res) {
                var data = res.data;
                // do any processing you need to do with data here
                return data;
            });
        };

        return res;
    });

You'd use it the same way you're currently using Participant in your code, via Participant.query() or Participant.get(). The data you return in the chained $then handler will be used to resolve the promise returned by $resource.

Intelekshual
  • 7,444
  • 1
  • 21
  • 28
  • Thanks for the answer. I'm not 100% sure, but I think `transformResponse` might be the easier way to go. – c0bra Apr 05 '13 at 20:13
  • 1
    @c0bra, Intelekshual's way is useful when you need to access variables that are not accessible in `transformResponse` – JacobF Jun 25 '14 at 11:29
  • Another advantage of this solution is we can reuse default methods from $resources to create our custom methods, and then transform responses easily – Vu Anh Oct 17 '17 at 03:46
0

The way I did it was by adding a service to the module:

angular.module('keeniolab', ['ngResource']).
factory('KeenIO',function ($resource) {
    // factory implementation
}).service('KeenModel', function (KeenIO) {
    var KeenSession = function () {
        this.data = {};
    };

    KeenSession.prototype.fetch = function (query) {
        var self = this;

        KeenIO.get(query, function (result) {
            self.data = result;
        });
    };

    return new KeenSession();
});

Now you can simply monitor the collection:

$scope.$watchCollection(function () {
    return KeenModel.data;
},
function (value) {
    // code here
});

Keen.IO Resource Factory with Service Model

Daniël W. Crompton
  • 3,448
  • 25
  • 26