3

I have implemented an angular <svgimg src="file"> directive to load .svg images inline, the link function uses $http to load the file which is then parsed to add and remove tags. The result is inline <svg> which can be coloured and sized with css.

This works fine, but since my app will have loads of small images this solution is not very efficient because angular calls the service multiple times per digest - which means the loading/parsing process is performed 100's of times ($http cache helped).

I am trying to replace the $http.get() part of the directive with a caching service.

The cache service maintains a list of already-loaded files and provides the ready-to-inject version.

My problem is in implementing the promises....

The app.directive() link function does:

link: function( scope, element, attrs ){
    var promise= svgService.get( svgFilename );  // was: $http.get(...)
    promise.then( 
        function(data){
            // sort injected attribs and then...
            element.replaceWith(data);
        },
        function(data){ /* error stuff */ } 
    );
};

which appears to be fine.

The service function is along the lines of:

app.service('svgCacheService', ['$q', '$http', function($q, $http){
    this.get=function( srcfile ){
        var deferred= $q.defer();

        if( srcfile in cache is loaded ){
            deferred.resolve( cache[srcfile] );
            return deferred.promise;        // ok, no problem with this
        }else{
            if( srcfile in cache marked as 'loading' ){
                // file is marked in cache, but is waiting for $http
                return SOME_PROMISE;        // tried allsorts, no luck yet :(
            }else{
                // file not in cache
                cache[srcfile]='loading';   // new cache entry, marked 'loading' 
                $http.get( srcfile )
                .success( function(data){
                    // xml parsing etc. goes in here
                    cache[srcfile]= data;

                    // how to resolve waiting promises waiting on 'loading'???

                    deferred.resolve( cache[srcfile] );
                    return deferred.promise;   // this caller is ok
                }
                .error( ... error stuff ... )
            }
        }
    }

This sort-of works, but only actually loads a few images (some are used multiple times on a page and the odd one does appear) - I think the problem seems to be that the SOME_PROMISE's issued in the case of 'cache is loading' are not resolved.

I have tried adding the deferred object in the cache and returning that to each caller, but it makes no difference.

Questions:

I'm guessing that I need to keep track of lots of separate promises... do I need to keep an array of SOME_PROMISE in the cache and then resolve them all when the $http succeeds ?

Generally, can you only return a given promise instance to one caller ?

Any hints/tips/links would be much appreciated... thanks.

TonyWilk
  • 1,447
  • 10
  • 13
  • Total size of all SVG files? SVGs are tiny compared to an image (read: Megabytes), I'd cache ALL SVG in browser (send the entire collection in ZIP or LZString) – Alvin K. Oct 04 '14 at 03:48
  • There's only about 250k in ~200 .svg files... I did think about pre-processing them but want to leave it to the client to cook up more images whenever they want. My caching is so I only have to process them once to insert into the DOM so css can do its stuff. – TonyWilk Oct 04 '14 at 04:31
  • Have you tried the [one time binding](http://stackoverflow.com/questions/15183095/angularjs-really-slow-at-rendering-with-about-2000-elements) to fix the loading/parsing? Try to fix the root cause first. – Alvin K. Oct 04 '14 at 17:38
  • one time binding does look interesting... I'll see if we can use the newer version of angular - thanks. – TonyWilk Oct 04 '14 at 17:41

0 Answers0