Blog>
Snippets

Implementing Offline-First Strategies using Angular PWA

Create an example using Angular PWA to implement an offline-first approach, including strategies like cache-first for specific resources.
import { Injectable } from '@angular/core';
import { SwUpdate } from '@angular/service-worker';

@Injectable({
  providedIn: 'root'
})
export class CheckForUpdateService {
  constructor(private swUpdate: SwUpdate) {
    this.swUpdate.available.subscribe(event => {
      if (confirm('New version available. Load New Version?')) {
        window.location.reload();
      }
    });
  }
}
This service subscribes to the SwUpdate service's available event, which emits whenever there is an update available. When it gets an update, it prompts the user, and on confirmation, reloads the page to activate the new version of the service worker and the app.
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  constructor(private http: HttpClient) {}

  getData() {
    return this.http.get('/api/data', { observe: 'response' });
  }
}
This DataService class provides a getData() method that performs an HTTP GET request to fetch data which can later be cached by the service worker.
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ServiceWorkerModule } from '@angular/service-worker';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    ServiceWorkerModule.register('ngsw-worker.js', {
      enabled: true,
      registrationStrategy: 'registerWhenStable:30000'
    })
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
AppModule imports the ServiceWorkerModule and registers the Angular Service Worker script. This registration configures the Angular PWA with a service worker which manages caching strategies and other PWA functionalities.
{
  "index": "/index.html",
  "assetGroups": [{
    "name": "app",
    "installMode": "prefetch",
    "resources": {
      "files": [
        "/favicon.ico",
        "/index.html",
        "/*.css",
        "/*.js"
      ]
    }
  }, {
    "name": "assets",
    "installMode": "lazy",
    "updateMode": "prefetch",
    "resources": {
      "files": [
        "/assets/**",
        "/*(eot|svg|ttf|woff|woff2|otf)"
      ],
      "urls": [
        "https://fonts.googleapis.com/**"
      ]
    }
  }]
}
This JSON configuration is part of the ngsw-config.json file which defines caching behaviors. The 'app' asset group is set to prefetch and contain the main application files. The 'assets' asset group has fonts and other assets to be loaded lazily with 'updateMode' set to 'prefetch' to update the cache proactively.
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  // other component logic
}
This AppComponent is a very basic Angular component structure, where the PWA capabilities would be integrated. The actual usage of caching strategies or offline management would usually happen in service classes and get reflected in components through Angular's databinding and template syntax.
@Injectable({
  providedIn: 'root'
})
export class CustomStrategyService {
  constructor(private swUpdate: SwUpdate) {
    this.swUpdate.versionUpdates.subscribe(update => {
      if (update.type === 'VERSION_READY') {
        this.activateUpdate(update);
      }
    });
  }

  private activateUpdate(update) {
    this.swUpdate.activateUpdate(update.version).then(() => window.location.reload());
  }
}
This CustomStrategyService shows versionUpdates subscription. When a new version is ready, it activates the update and reloads the page to ensure the latest version of files are loaded. This is useful when defining custom strategies.