angularjs $resource class-level callbacks, or post-processing
Asked Answered
H

3

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);
            }
        };
    });
Herwick answered 5/4, 2013 at 19:8 Comment(1)
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)Nicotine
H
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.

Herwick answered 5/4, 2013 at 20:8 Comment(3)
Thanks, I like the $http.defaults.transformResponse.concat part.Rejoice
Please note: you can't use AJAX request inside transformResponse to modify fields of data objectQuattlebaum
I had to add a return statement at the end of the factory function to make it work.Wakeen
E
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.

Experiment answered 5/4, 2013 at 20:0 Comment(3)
Thanks for the answer. I'm not 100% sure, but I think transformResponse might be the easier way to go.Herwick
@c0bra, Intelekshual's way is useful when you need to access variables that are not accessible in transformResponseRejoice
Another advantage of this solution is we can reuse default methods from $resources to create our custom methods, and then transform responses easilyHintze
L
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

Lindgren answered 6/6, 2013 at 12:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.