๐—•๐˜‚๐—ถ๐—น๐—ฑ๐—ถ๐—ป๐—ด ๐—ฎ ๐—ฃ๐—ช๐—” ๐˜„๐—ถ๐˜๐—ต ๐—”๐—ป๐—ด๐˜‚๐—น๐—ฎ๐—ฟ ๐Ÿญ๐Ÿณ: ๐—ข๐—ณ๐—ณ๐—น๐—ถ๐—ป๐—ฒ-๐—™๐—ถ๐—ฟ๐˜€๐˜ ๐—ฎ๐—ป๐—ฑ ๐—•๐—ฎ๐—ฐ๐—ธ๐—ด๐—ฟ๐—ผ๐˜‚๐—ป๐—ฑ ๐—ฆ๐˜†๐—ป๐—ฐ ๐˜„๐—ถ๐˜๐—ต ๐—ฆ๐—ฒ๐—ฟ๐˜ƒ๐—ถ๐—ฐ๐—ฒ ๐—ช๐—ผ๐—ฟ๐—ธ๐—ฒ๐—ฟ๐˜€

Stephen Oloto
6 min readJan 13, 2025

pwa-freepik-stephen-redraw-banner

In todayโ€™s fast-paced digital world, delivering seamless user experiences is crucial. One powerful tool that enhances the performance and reliability of web applications is ๐—ฆ๐—ฒ๐—ฟ๐˜ƒ๐—ถ๐—ฐ๐—ฒ ๐—ช๐—ผ๐—ฟ๐—ธ๐—ฒ๐—ฟ๐˜€.

Service Workers are JavaScript scripts that run independently in the background, separate from the main browser thread. While many resources cover PWAs, Angularโ€™s built-in service worker support simplifies the process of building one.

This inspired me to create an Angular clone of the job search app from my previous React Portal Tutorial. In this guide, weโ€™ll transform the Angular 17 Job search application into a PWA, incorporating offline caching and background synchronization to keep your app functional during periods of limited or no connectivity.

To further enhance offline capabilities, weโ€™ll make use of ๐—œ๐—ป๐—ฑ๐—ฒ๐˜…๐—ฒ๐—ฑ๐——๐—• for retrieving cached data, providing smooth and efficient access to information regardless of network availability.

๐—ช๐—ต๐˜† ๐—–๐—ต๐—ผ๐—ผ๐˜€๐—ฒ ๐—”๐—ป๐—ด๐˜‚๐—น๐—ฎ๐—ฟ ๐—ณ๐—ผ๐—ฟ ๐—ฃ๐—ช๐—”๐˜€?
Angular provides an out-of-the-box solution with the @๐™–๐™ฃ๐™œ๐™ช๐™ก๐™–๐™ง/๐™ฅ๐™ฌ๐™– package, making it easy to add PWA features to your application. It includes:

โ€ข ๐—ฆ๐—ฒ๐—ฟ๐˜ƒ๐—ถ๐—ฐ๐—ฒ ๐˜„๐—ผ๐—ฟ๐—ธ๐—ฒ๐—ฟ ๐—ฟ๐—ฒ๐—ด๐—ถ๐˜€๐˜๐—ฟ๐—ฎ๐˜๐—ถ๐—ผ๐—ป.
โ€ข ๐—ฃ๐—ฟ๐—ฒ๐—ฐ๐—ผ๐—ป๐—ณ๐—ถ๐—ด๐˜‚๐—ฟ๐—ฒ๐—ฑ ๐—ฐ๐—ฎ๐—ฐ๐—ต๐—ถ๐—ป๐—ด ๐˜€๐˜๐—ฟ๐—ฎ๐˜๐—ฒ๐—ด๐—ถ๐—ฒ๐˜€.
โ€ข ๐—ฆ๐˜‚๐—ฝ๐—ฝ๐—ผ๐—ฟ๐˜ ๐—ณ๐—ผ๐—ฟ ๐—ฎ๐—ฝ๐—ฝ ๐—บ๐—ฎ๐—ป๐—ถ๐—ณ๐—ฒ๐˜€๐˜ ๐—ณ๐—ถ๐—น๐—ฒ๐˜€.

Step 1: Setting Up the Angular Job Search Application
Create a new Angular project:

ng new JobSearch-NG

Step 2: Add Angular PWA Support:

ng add @angular/pwa

This command:
โ€ข Generates a manifest.webmanifest file for your app.
โ€ข Adds a ngsw-config.json file for configuring service workers.
โ€ข Updates angular.json to include service worker support.
โ€ข Registers the service worker in the app config.

Step 3: Configure ๐—ป๐—ด๐˜€๐˜„-๐—ฐ๐—ผ๐—ป๐—ณ๐—ถ๐—ด.๐—ท๐˜€๐—ผ๐—ป for Offline Caching
Update the ngsw-config.json file to define caching strategies for your API and static assets.

โ€ข ๐—ฎ๐˜€๐˜€๐—ฒ๐˜๐—š๐—ฟ๐—ผ๐˜‚๐—ฝ๐˜€: Caches static assets like CSS, JS, and images.
โ€ข ๐—ฑ๐—ฎ๐˜๐—ฎ๐—š๐—ฟ๐—ผ๐˜‚๐—ฝ๐˜€: Caches API requests with freshness strategy for one day.

Step 4: Create a Fetch Service with Signals

In the React version of the application, we utilized the useFetch hook to abstract API calls into a reusable function. Similarly, in this Angular implementation, we leverage signals to manage application state in a streamlined and efficient manner.

Angularโ€™s signals offer a powerful way to track and respond to changes in state, eliminating the need for manual change detection. By reducing unnecessary re-renders, signals contribute to faster and more responsive applications.

Here, we use signals to manage API data, loading states, and errors:

Step 5: Implement Background Sync
Use the Background Sync API to retry failed API requests when the network is back.

๐—–๐—ฟ๐—ฒ๐—ฎ๐˜๐—ฒ ๐—ฎ ๐—•๐—ฎ๐—ฐ๐—ธ๐—ด๐—ฟ๐—ผ๐˜‚๐—ป๐—ฑ ๐—ฆ๐˜†๐—ป๐—ฐ ๐—ฆ๐—ฒ๐—ฟ๐˜ƒ๐—ถ๐—ฐ๐—ฒ

While implementing this, I discovered that the sync property is not directly available in the ServiceWorkerRegistration class. To work around this, we cast it to any and treat it as a BackgroundSyncManager.

Before accessing the sync property, we first ensure that both the serviceWorker and SyncManager APIs are supported by the browser.
After that, we proceed register the tag that will be listened to in our service worker javascript file to know when to sync the cached data. Since the SyncManager API is not universally supported, itโ€™s crucial to check compatibility using resources like Can I Use.

Additionally, we added a catch block to the .register() promise to gracefully handle potential registration failures.

To maintain clean and readable code, we encapsulated the casting logic in a getServiceWorkerSync method, providing a centralized and reusable solution.

๐—›๐—ฎ๐—ป๐—ฑ๐—น๐—ถ๐—ป๐—ด ๐—”๐—ฃ๐—œ ๐—™๐—ฎ๐—ถ๐—น๐˜‚๐—ฟ๐—ฒ ๐—ช๐—ถ๐˜๐—ต ๐—•๐—ฎ๐—ฐ๐—ธ๐—ด๐—ฟ๐—ผ๐˜‚๐—ป๐—ฑ ๐—ฆ๐˜†๐—ป๐—ฐ

To improve the user experience during API failures, we invoke the registerBackgroundSync method within the error block of our side effect in the signal of fetch.service.ts. This logic is triggered in the ngOnInit lifecycle hook of App.component.ts.

When an API call fails โ€” whether due to network issues or other factors โ€” the service worker takes over and syncs the offline data seamlessly, preventing the display of an error page and ensuring a more resilient application behavior.

๐—จ๐—ฝ๐—ฑ๐—ฎ๐˜๐—ฒ ๐˜๐—ต๐—ฒ ๐—ฆ๐—ฒ๐—ฟ๐˜ƒ๐—ถ๐—ฐ๐—ฒ ๐—ช๐—ผ๐—ฟ๐—ธ๐—ฒ๐—ฟ ๐˜๐—ผ ๐—›๐—ฎ๐—ป๐—ฑ๐—น๐—ฒ ๐—•๐—ฎ๐—ฐ๐—ธ๐—ด๐—ฟ๐—ผ๐˜‚๐—ป๐—ฑ ๐—ฆ๐˜†๐—ป๐—ฐ

Since we cannot directly modify the ngsw-worker.js as its generated at build time, we create a custom service worker ๐˜€๐—ฒ๐—ฟ๐˜ƒ๐—ถ๐—ฐ๐—ฒ-๐˜„๐—ผ๐—ฟ๐—ธ๐—ฒ๐—ฟ.j๐˜€ in our src folder to handle background sync.

We add an event listener to listen for a sync event with the tag ๐˜€๐˜†๐—ป๐—ฐ-๐—ท๐—ผ๐—ฏ๐˜€ specified in the App.component.ts so that when the browser regains connectivity, the sync-jobs event triggers.

The Service Worker fetches the latest jobs from the API
and the Jobs are stored in the IndexedDB, making them available for offline use. so with this we ensure seamless background synchronization and offline caching for the application.

Step 6: Use IndexedDB for Offline Data Storage
Next, we create ๐—ท๐—ผ๐—ฏ-๐—ฑ๐—ฎ๐˜๐—ฎ๐—ฏ๐—ฎ๐˜€๐—ฒ.๐˜€๐—ฒ๐—ฟ๐˜ƒ๐—ถ๐—ฐ๐—ฒ where we use ๐—œ๐—ป๐—ฑ๐—ฒ๐˜…๐—ฒ๐—ฑ๐——๐—• to store job data locally for offline use. IndexedDB is a low-level, asynchronous database API built into modern browsers.

Install idb for IndexedDB Management

npm install idb

JobDatabaseService for IndexedDB Operations

Here, we define a ๐—๐—ผ๐—ฏ๐——๐—• schema with a ๐—ท๐—ผ๐—ฏ๐˜€ object store, where each job is identified by a unique ๐—ท๐—ผ๐—ฏ_๐—ถ๐—ฑ.

The database is created or opened using ๐—ผ๐—ฝ๐—ฒ๐—ป๐——๐—•, and during initialization, the ๐˜‚๐—ฝ๐—ด๐—ฟ๐—ฎ๐—ฑ๐—ฒ callback ensures the ๐—ท๐—ผ๐—ฏ๐˜€ object store is set up with ๐—ท๐—ผ๐—ฏ_๐—ถ๐—ฑ as the primary key. The service provides two key methods:

1. ๐—ฎ๐—ฑ๐—ฑ๐—๐—ผ๐—ฏ: Adds a new job or updates an existing one in the database using the ๐™ฅ๐™ช๐™ฉ method.
2. ๐—ด๐—ฒ๐˜๐—๐—ผ๐—ฏ๐˜€: Retrieves all stored jobs using ๐™œ๐™š๐™ฉ๐˜ผ๐™ก๐™ก.

Step 7: Fetch Jobs from Cache or IndexedDB

Next, we update the FetchService to fetch jobs from IndexedDB when offline, we used ๐—ป๐—ฎ๐˜ƒ๐—ถ๐—ด๐—ฎ๐˜๐—ผ๐—ฟ.๐—ผ๐—ป๐—Ÿ๐—ถ๐—ป๐—ฒ to determine if the app is offline. When offline, jobs are fetched from IndexedDB using ๐—ท๐—ผ๐—ฏ๐——๐—ฏ.๐—ด๐—ฒ๐˜๐—๐—ผ๐—ฏ๐˜€().
On a successful API call, jobs are saved to IndexedDB using ๐—ท๐—ผ๐—ฏ๐——๐—ฏ.๐—ฎ๐—ฑ๐—ฑ๐—๐—ผ๐—ฏ(๐—ท๐—ผ๐—ฏ). then we ensure ๐—ฑ๐—ฎ๐˜๐—ฎ, ๐—ถ๐˜€๐—Ÿ๐—ผ๐—ฎ๐—ฑ๐—ถ๐—ป๐—ด, and ๐—ฒ๐—ฟ๐—ฟ๐—ผ๐—ฟ signals are updated consistently for offline and online cases.

And weโ€™re done!.

๐—ง๐—ฒ๐˜€๐˜๐—ถ๐—ป๐—ด ๐—ฎ๐—ป๐—ฑ ๐——๐—ฒ๐—ฏ๐˜‚๐—ด๐—ด๐—ถ๐—ป๐—ด

1. Test Offline Functionality
Run your app in production mode:

ng build

Copy your service-worker.js file into the dist/job-search-ng folder (alternatively, you can write a script in your package.json that automatically copies the file into the specified folder after the build is completed) then use http-server to load your bundled application;

npx http-server -p 8080 -c-l dist/job-search-ng

Open Developer Tools > ๐—”๐—ฝ๐—ฝ๐—น๐—ถ๐—ฐ๐—ฎ๐˜๐—ถ๐—ผ๐—ป > ๐—ฆ๐—ฒ๐—ฟ๐˜ƒ๐—ถ๐—ฐ๐—ฒ ๐—ช๐—ผ๐—ฟ๐—ธ๐—ฒ๐—ฟ๐˜€.
Simulate offline mode and test functionality.

indexedBD entries

2. Debug Background Sync
Use the Sync section under Application in Developer Tools to debug sync events.

debugging sync events

๐—–๐—ผ๐—ป๐—ฐ๐—น๐˜‚๐˜€๐—ถ๐—ผ๐—ป
Youโ€™ve successfully created a PWA using Angular! By integrating service workers, background sync, and offline caching, your app is now resilient to network issues. PWAs not only enhance user experience but also make your app accessible anytime, anywhere.

Explore the full code for this tutorial on GitHub.

Donโ€™t forget to Follow me to get more guides on how to build scalable and user-centric solutions. Thanks๐Ÿ˜๐Ÿ˜Š and Enjoy !!!

๐—ก๐—ฒ๐˜…๐˜ ๐—ฆ๐˜๐—ฒ๐—ฝ๐˜€
โ€ข Add push notifications for real-time updates.
โ€ข Optimize caching strategies for large-scale applications.
โ€ข Publish your app to a web hosting service for public access.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Stephen Oloto
Stephen Oloto

Written by Stephen Oloto

An Innovative Software Engineer with 5+ years' experience designing and launching responsive websites, fintech solutions, and e-commerce platforms.

Responses (1)

Write a response

Great job

1

More from Stephen Oloto