Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

angularjs $resource class-level callbacks, or post-processing

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);
            }
        };
    });
like image 394
c0bra Avatar asked Apr 05 '13 19:04

c0bra


2 Answers

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.

like image 159
c0bra Avatar answered Nov 16 '22 22:11

c0bra


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.

like image 8
Intelekshual Avatar answered Nov 16 '22 20:11

Intelekshual