Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Caching Google Maps API with @angular/service-worker

I'm working on an Angular 5 project and want to provide PWA functionality with the @angular/service-worker package.

I can't manage to cache and serve Google Maps API requests (e.g. tiles). The responses aren't cached when online, therefore also not served when offline.

What I tried:

  • Adding Maps URL to dataGroups: No Error, but also no cache, when going offline I get the following Errors:

    ERROR Error: Uncaught (in promise): Event: {"isTrusted":true} (in the main.bundle.js)

  • Adding Maps URL to assetGroups installMode: prefetch, where I get cross-origin errors when trying to prefetch:

    No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

  • Adding Maps URL to assetGroups installMode: lazy, where I get the same result as installMode: prefetch

The rest of the data caches and serves well (static from localhost & json from localhost API endpoint on a different port).

My ngsw-config.json looks like this: Any pointers are highly appreciated.

{
  "index": "/index.html",
  "assetGroups": [{
    "name": "app",
    "installMode": "prefetch",
    "resources": {
      "files": [
        "/favicon.png",
        "/index.html"
      ],
      "versionedFiles": [
        "/*.bundle.css",
        "/*.bundle.js",
        "/*.chunk.js"
      ],
      "urls": [
          "https://fonts.googleapis.com/css?family=Roboto:300,400,500,700",
          "https://fonts.gstatic.com/s/roboto/v18/KFOlCnqEu92Fr1MmEU9fBBc4AMP6lQ.woff2",
          "https://fonts.gstatic.com/s/roboto/v18/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2"
      ]
    }
  }, {
    "name": "assets",
    "installMode": "lazy",
    "updateMode": "prefetch",
    "resources": {
      "files": [
        "/assets/**"
      ]
    }
  }],
    "dataGroups": [{
        "name": "from-api",
        "urls": [
            "/api/restaurants",
            "/img/**",
            "/icons/**",
            "https://maps.googleapis.com/maps/**"
        ],
        "cacheConfig": {
            "maxSize": 100,
            "maxAge": "1d",
            "timeout?": "3s"
        }
    }]
}

Happy to provide more information if needed.

like image 957
Juri Avatar asked Mar 01 '18 23:03

Juri


People also ask

Can you cache Google Maps API?

Applications using the Places API are bound by the terms of your Agreement with Google. Subject to the terms of your Agreement, you must not pre-fetch, index, store, or cache any Content except under the limited conditions stated in the terms.

Which service worker caching strategies supported by angular?

The Angular service worker can use either of two caching strategies for data resources. The default, optimizes for responses that are as fast as possible. If a resource exists in the cache, the cached version is used, and no network request is made.

Does angular cache assets?

The tricky part with serving Angular applications is caching the static asset files. Some frameworks, depending on the Webpack configuration and the way that assets are used, fingerprint all images and other static assets. In Angular, static assets, like images and custom fonts, are placed in the src/assets/ directory.


2 Answers

I'm working with service worker with google maps too and works for if I insert it "https://maps.googleapis.com/maps/**" at assetGroups -> urls:

"urls": [
  "https://fonts.googleapis.com/css?family=Roboto:300,400,500,700",
  "https://fonts.gstatic.com/s/roboto/v18/KFOlCnqEu92Fr1MmEU9fBBc4AMP6lQ.woff2",
  "https://fonts.gstatic.com/s/roboto/v18/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2",
  "https://maps.googleapis.com/maps/**"
]
like image 184
Pedro Arantes Avatar answered Sep 21 '22 20:09

Pedro Arantes


Basically, Google maps only accepts no-cors requests. If you configured the request to https:// maps.googleapis.com to no-cors mode, that request would be rejected by the server.

You must allow only no-cors requests then Google can ensure third-party sites are free to read data from it, but are limited in their ability to modify the requests:

So, I do this...

var googleMapsAPIJS = "https://maps.googleapis.com/maps/api/js?key =" + YOUR KEY& callback = initMap";
if (requestURL.href = = = googleMapsAPIJS) { 
    event.respondWith(
     fetch( 
    googleMapsAPIJS +"&" + Date.now(), 
        { mode: "no-cors", cache: "no-store" } 
     ).catch( function() {
//so if offline serve my static map
      return caches.match("/offline-map.html"); 
})
); 
}

Because Google’s servers return the Maps API JavaScript file with headers that cause the browser to always attempt to return it from the HTTP cache, we have to make sure it is always fetched from the network. Otherwise, our fetch won’t fail, and we would get the Google Maps controls (from the cache), but no map under it (the map data isn’t cached). We accomplish this by fetching with the cache option set to no-store, which skips the cache completely.

Unfortunately, at the time of writing, this option is still not supported in all browsers, so add a cache-busting timestamp to each request’s query string to make sure each request is unique and will skip the cache. We do this by appending the current time to each request’s URL.

This is a modification of a solution I read in a great book: Ater, Tal. Building Progressive Web Apps: Bringing the Power of Native to the Browser O'Reilly Media.

like image 39
Vince Stack Avatar answered Sep 21 '22 20:09

Vince Stack