Last Updated: January 28, 2019
·
1.51K
· kfwerf

Abstract Service object with auto caching for AngularJS

This is a great little tool if you like to have control over cached services from the $http. It checks if caching is needed and determines from there if it needs to reload or not. This is very handy for super slow XHR feeds.

I am not sure if it already uses localstorage or not, but else this could easily be added to make it cache more.

The abstracted service:

var getAbstractService = function( options ) {
    var oOptions = options || {},
        oServiceObject = {
        _options: oOptions,
        _name: 'Abstract service object',
        _description: 'Returns a abstract service object which can cache out of the box.',
        _cache: {
            available: false,
            data: {},
            arguments: {}
        },
        _matchesCachedArguments: function() {
            var i = 0,
            isValid = true;

            for( i in this._options.arguments ) {
                if( this._options.arguments[i] !== this._cache.arguments[i] ) {
                    isValid = false;
                }
            }

            return isValid;
        },
        _isCacheMatching: function() {
            var isCacheMatching = false;

            if( this._cache.available && this._matchesCachedArguments() ) {
                isCacheMatching = true;
            }

            return isCacheMatching;
        },
        _loadCache: function() {
            setTimeout( function() {
                oServiceObject.setCallbackProcessing( {data: oServiceObject._cache.data, cached: true} );
                oServiceObject._callEventEnd( oServiceObject._cache.data );
            }, 1); // Avoid bugs by simulated a load time of 1ms
        },
        _getArgumentString: function() {
            var argumentString = '',
            i = 0;
            for( i in this._options.arguments ) {
                argumentString += '&' + i + '=' + this._options.arguments[i];
            }

            if( argumentString.length ) {
                argumentString = '?' + argumentString.slice(1);
            }

            return argumentString;
        },
        _loadService: function() {
            setLoadingState(true);
            $http.get( this._options.service  + this._getArgumentString() ).success( function( json ) {

                oServiceObject._cache.available = true;
                oServiceObject._cache.data = json;
                oServiceObject._cache.arguments = {};

                angular.extend(oServiceObject._cache.arguments, oOptions.arguments);

                setTimeout( function() {

                    oServiceObject.setCallbackProcessing( {data: json, cached: false} );

                    oServiceObject._callEventEnd( json );

                }, 1000); // simulate a 1000 ms latency

            })
            .error(function() {
                $rootScope.$broadcast(oServiceObject._options.name + '.UPDATE', { success: false, response: false });
                $rootScope.$broadcast(oServiceObject._options.name + '.ERROR', { success: false, response: false });

                setLoadingState( false );
            });
        },
        _callEventEnd: function( json ) {
            $rootScope.$broadcast(this._options.name + '.UPDATE', { success: true, response: json });
            $rootScope.$broadcast(this._options.name + '.SUCCESS', { success: true, response: json });

            setLoadingState( false );
        },
        setCallbackProcessing: function( options ) {}, // Empty placeholder function to be populated by host
        setServiceURL: function(url) {
            this._options.service = url;
        },
        getService: function( args ) { // Sort of a get instance..
            $rootScope.$broadcast( this._options.name + '.INIT' );

            this._options.arguments = args;
            console.log( this._options.name, this._cache, this._options );
            console.log( 'using cache: ' + this._isCacheMatching() );
            if( this._isCacheMatching() ) {
                this._loadCache();
            } else {
                this._loadService();
            }
        }
    };

    return oServiceObject;
}

A more defined example which envokes the abstract service:

Note: i put the AbstractService in a utilities js file where i call it from. i then extend it sort of in a factory. I just create it as a new var so i have freedom to create around what i want. It is supposed to be super lightweight.

/*  
    Defined service from abstract caching service.
    --
    It first sets up the abstract service inside as a new service, not extending it.
    After that it exposes the getService with some addons and returns it.
 */
angular.module( 'myApp' )
    .factory( 'superService', [
        '$http', '$rootScope', ''$routeParams',
        function( $http, $rootScope, $routeParams ) {
            'use strict';

            var $service = new $utilities.getAbstractService({
                name: 'SUPERSERVICE',
                service: $rootScope.SERVICEPATH + 'SuperService/' + $rootScope.SERVICELANG
            }),



            publicApi = {
                getService: function( $scope, args ) {

                    // Some more params

                    $service.getService( args );
                }
            };



            return publicApi;

        }
    ]);

I used this for when i had to switch pages and come back to a old page. I wanted this to be loaded from the factory, rather than reloading if the service was similar. It is mostly for shared services with multiple pages where you can land on.

It can also be used to implement a localstorage for familiar services.

Cheers,
Kenneth