diff --git a/aio/content/examples/examples.bzl b/aio/content/examples/examples.bzl index 81fb37a22c5a9..a7f389f4ac29b 100644 --- a/aio/content/examples/examples.bzl +++ b/aio/content/examples/examples.bzl @@ -88,7 +88,7 @@ EXAMPLES = { "rx-library": {"stackblitz": True, "zip": True}, "schematics-for-libraries": {"stackblitz": False, "zip": True}, "security": {"stackblitz": True, "zip": True}, - "service-worker-getting-started": {"stackblitz": False, "zip": False}, + "service-worker-getting-started": {"stackblitz": False, "zip": True}, "setup": {"stackblitz": False, "zip": False}, "structural-directives": {"stackblitz": True, "zip": True}, "styleguide": {"stackblitz": False, "zip": False}, diff --git a/aio/content/examples/service-worker-getting-started/BUILD.bazel b/aio/content/examples/service-worker-getting-started/BUILD.bazel index 2f531c17ae231..427a3a1141225 100644 --- a/aio/content/examples/service-worker-getting-started/BUILD.bazel +++ b/aio/content/examples/service-worker-getting-started/BUILD.bazel @@ -4,4 +4,5 @@ package(default_visibility = ["//visibility:public"]) docs_example( name = "service-worker-getting-started", + test = False, ) diff --git a/aio/content/examples/service-worker-getting-started/ngsw-config.json b/aio/content/examples/service-worker-getting-started/ngsw-config.json deleted file mode 100644 index 19133dc879f3a..0000000000000 --- a/aio/content/examples/service-worker-getting-started/ngsw-config.json +++ /dev/null @@ -1,25 +0,0 @@ - -{ - "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/**" - ] - } - }] -} diff --git a/aio/content/examples/service-worker-getting-started/src/app/app.component.html b/aio/content/examples/service-worker-getting-started/src/app/app.component.html index d63b39c22d30b..24f16a54b44ed 100755 --- a/aio/content/examples/service-worker-getting-started/src/app/app.component.html +++ b/aio/content/examples/service-worker-getting-started/src/app/app.component.html @@ -12,10 +12,10 @@

Here are some links to help you start:

- diff --git a/aio/content/examples/service-worker-getting-started/src/app/app.component.ts b/aio/content/examples/service-worker-getting-started/src/app/app.component.ts index a2aa3028c55b8..d0dc473a4b4db 100755 --- a/aio/content/examples/service-worker-getting-started/src/app/app.component.ts +++ b/aio/content/examples/service-worker-getting-started/src/app/app.component.ts @@ -2,6 +2,7 @@ import { Component } from '@angular/core'; import { SwUpdate } from '@angular/service-worker'; @Component({ + standalone: true, selector: 'app-root', templateUrl: './app.component.html' }) diff --git a/aio/content/examples/service-worker-getting-started/src/app/app.module.ts b/aio/content/examples/service-worker-getting-started/src/app/app.module.ts deleted file mode 100755 index 05b66c2cde971..0000000000000 --- a/aio/content/examples/service-worker-getting-started/src/app/app.module.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule, isDevMode } from '@angular/core'; -import { AppComponent } from './app.component'; - -import { ServiceWorkerModule } from '@angular/service-worker'; - -import { CheckForUpdateService } from './check-for-update.service'; -import { LogUpdateService } from './log-update.service'; -import { PromptUpdateService } from './prompt-update.service'; - -@NgModule({ - declarations: [ - AppComponent - ], - imports: [ - BrowserModule, - ServiceWorkerModule.register('/ngsw-worker.js', {enabled: !isDevMode()}) - ], - providers: [ - CheckForUpdateService, - LogUpdateService, - PromptUpdateService, - ], - bootstrap: [AppComponent] -}) -export class AppModule { } diff --git a/aio/content/examples/service-worker-getting-started/src/app/check-for-update.service.ts b/aio/content/examples/service-worker-getting-started/src/app/check-for-update.service.ts index dab9b69a448ab..2a575a047c791 100755 --- a/aio/content/examples/service-worker-getting-started/src/app/check-for-update.service.ts +++ b/aio/content/examples/service-worker-getting-started/src/app/check-for-update.service.ts @@ -3,7 +3,7 @@ import { SwUpdate } from '@angular/service-worker'; import { concat, interval } from 'rxjs'; import { first } from 'rxjs/operators'; -@Injectable() +@Injectable({providedIn: 'root'}) export class CheckForUpdateService { constructor(appRef: ApplicationRef, updates: SwUpdate) { diff --git a/aio/content/examples/service-worker-getting-started/src/app/handle-unrecoverable-state.service.ts b/aio/content/examples/service-worker-getting-started/src/app/handle-unrecoverable-state.service.ts index ce73d22480e89..b22260a22b9ae 100644 --- a/aio/content/examples/service-worker-getting-started/src/app/handle-unrecoverable-state.service.ts +++ b/aio/content/examples/service-worker-getting-started/src/app/handle-unrecoverable-state.service.ts @@ -4,7 +4,7 @@ import { SwUpdate } from '@angular/service-worker'; function notifyUser(message: string): void { } // #docregion sw-unrecoverable-state -@Injectable() +@Injectable({providedIn: 'root'}) export class HandleUnrecoverableStateService { constructor(updates: SwUpdate) { updates.unrecoverable.subscribe(event => { diff --git a/aio/content/examples/service-worker-getting-started/src/app/log-update.service.ts b/aio/content/examples/service-worker-getting-started/src/app/log-update.service.ts index ddb4f9ea60f3a..1bb2c6a65c34f 100755 --- a/aio/content/examples/service-worker-getting-started/src/app/log-update.service.ts +++ b/aio/content/examples/service-worker-getting-started/src/app/log-update.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { SwUpdate, VersionReadyEvent } from '@angular/service-worker'; // #docregion sw-update -@Injectable() +@Injectable({providedIn: 'root'}) export class LogUpdateService { constructor(updates: SwUpdate) { diff --git a/aio/content/examples/service-worker-getting-started/src/app/prompt-update.service.ts b/aio/content/examples/service-worker-getting-started/src/app/prompt-update.service.ts index 78eeac2bb6b71..45059eda7a681 100755 --- a/aio/content/examples/service-worker-getting-started/src/app/prompt-update.service.ts +++ b/aio/content/examples/service-worker-getting-started/src/app/prompt-update.service.ts @@ -10,7 +10,7 @@ function promptUser(event: VersionReadyEvent): boolean { } // #docregion sw-version-ready -@Injectable() +@Injectable({providedIn: 'root'}) export class PromptUpdateService { constructor(swUpdate: SwUpdate) { diff --git a/aio/content/examples/service-worker-getting-started/src/main.ts b/aio/content/examples/service-worker-getting-started/src/main.ts index 0a621147e305c..964eff71b7710 100755 --- a/aio/content/examples/service-worker-getting-started/src/main.ts +++ b/aio/content/examples/service-worker-getting-started/src/main.ts @@ -1,6 +1,9 @@ -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { bootstrapApplication, provideProtractorTestingSupport } from '@angular/platform-browser'; -import { AppModule } from './app/app.module'; +import { AppComponent } from './app/app.component'; -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.error(err)); +bootstrapApplication(AppComponent, { + providers: [ + provideProtractorTestingSupport(), // essential for e2e testing + ] +}); diff --git a/aio/content/examples/service-worker-getting-started/zipper.json b/aio/content/examples/service-worker-getting-started/zipper.json new file mode 100644 index 0000000000000..ce44927d99cc7 --- /dev/null +++ b/aio/content/examples/service-worker-getting-started/zipper.json @@ -0,0 +1,7 @@ +{ + "files": [ + "!dist/", + "!**/*.d.ts", + "!**/src/**/*.js" + ] +} diff --git a/aio/content/guide/service-worker-communications.md b/aio/content/guide/service-worker-communications.md index d5d4f9aa7d0c5..78c6b1997c614 100644 --- a/aio/content/guide/service-worker-communications.md +++ b/aio/content/guide/service-worker-communications.md @@ -1,6 +1,6 @@ # Service worker communication -Importing `ServiceWorkerModule` into your `AppModule` doesn't just register the service worker, it also provides a few services you can use to interact with the service worker and control the caching of your application. +Enabling service worker support does more than just register the service worker; it also provides services you can use to interact with the service worker and control the caching of your application. ## Prerequisites @@ -48,7 +48,7 @@ The check might fail, which will cause a rejection of the `Promise`.
-In order to avoid negatively affecting the initial rendering of the page, `ServiceWorkerModule` waits for up to 30 seconds by default for the application to stabilize, before registering the ServiceWorker script. +In order to avoid negatively affecting the initial rendering of the page, by default the Angular service worker service waits for up to 30 seconds for the application to stabilize before registering the ServiceWorker script. Constantly polling for updates, for example, with [setInterval()](https://developer.mozilla.org/docs/Web/API/WindowOrWorkerGlobalScope/setInterval) or RxJS' [interval()](https://rxjs.dev/api/index/function/interval), prevents the application from stabilizing and the ServiceWorker script is not registered with the browser until the 30 seconds upper limit is reached.
diff --git a/aio/content/guide/service-worker-config.md b/aio/content/guide/service-worker-config.md index 693c7327ef39a..1770401aaa2b0 100644 --- a/aio/content/guide/service-worker-config.md +++ b/aio/content/guide/service-worker-config.md @@ -9,8 +9,13 @@ A basic understanding of the following: * [Service worker overview](https://developer.chrome.com/docs/workbox/service-worker-overview/) * [Service Worker in Production](guide/service-worker-devops) -The `ngsw-config.json` configuration file specifies which files and data URLs the Angular service worker should cache and how it should update the cached files and data. -The [Angular CLI](cli) processes the configuration file during `ng build`. +## Modifying the configuration + +The `ngsw-config.json` JSON configuration file specifies which files and data URLs the Angular service worker should cache and how it should update the cached files and data. +The [Angular CLI](cli) processes this configuration file during `ng build`. + + All file paths must begin with `/`, which corresponds to the deployment directory —usually `dist/` in CLI projects. @@ -451,4 +457,4 @@ It is recommended that you use the default performance strategy whenever possibl -@reviewed 2022-02-28 +@reviewed 2023-09-06 diff --git a/aio/content/guide/service-worker-devops.md b/aio/content/guide/service-worker-devops.md index 2334ea38d55b0..c0d9a79930000 100644 --- a/aio/content/guide/service-worker-devops.md +++ b/aio/content/guide/service-worker-devops.md @@ -33,7 +33,7 @@ Any time this version of `index.html` is served, the corresponding `bundle.js` m For example, assume that the `startApp()` function is renamed to `runApp()` in both files. In this scenario, it is not valid to serve the old `index.html`, which calls `startApp()`, along with the new bundle, which defines `runApp()`. -This file integrity is especially important when lazy loading modules. +This file integrity is especially important when lazy loading. A JS bundle might reference many lazy chunks, and the filenames of the lazy chunks are unique to the particular build of the application. If a running application at version `X` attempts to load a lazy chunk, but the server has already updated to version `X + 1`, the lazy loading operation fails. @@ -354,4 +354,4 @@ You might also be interested in the following: -@reviewed 2022-02-28 +@reviewed 2023-09-06 diff --git a/aio/content/guide/service-worker-getting-started.md b/aio/content/guide/service-worker-getting-started.md index dff408b92ecd0..7778fd3e7a7e2 100644 --- a/aio/content/guide/service-worker-getting-started.md +++ b/aio/content/guide/service-worker-getting-started.md @@ -7,30 +7,31 @@ It then uses an example to show you a service worker in action, demonstrating lo A basic understanding of the information in [Introduction to Angular service workers](guide/service-worker-intro). + + ## Adding a service worker to your project -To set up the Angular service worker in your project, use the CLI command `ng add @angular/pwa`. -It takes care of configuring your application to use service workers by adding the `@angular/service-worker` package along -with setting up the necessary support files. +To set up the Angular service worker in your project, run the following CLI command. -ng add @angular/pwa --project <project-name> +ng add @angular/pwa -The preceding command completes the following actions: +The CLI configures your application to use service workers with the following actions: 1. Adds the `@angular/service-worker` package to your project. 1. Enables service worker build support in the CLI. -1. Imports and registers the service worker in the application module. +1. Imports and registers the service worker with the application's root providers. 1. Updates the `index.html` file: * Includes a link to add the `manifest.webmanifest` file * Adds a meta tag for `theme-color` 1. Installs icon files to support the installed Progressive Web App \(PWA\). -1. Creates the service worker configuration file called [`ngsw-config.json`](guide/service-worker-config), which specifies the caching behaviors and other settings. +1. Creates the service worker configuration file called [`ngsw-config.json`](guide/service-worker-config), +which specifies the caching behaviors and other settings. - Now, build the project: +Now, build the project: @@ -45,9 +46,36 @@ The CLI project is now set up to use the Angular service worker. This section demonstrates a service worker in action, using an example application. +
+ +To play along, +download the example code. + +Unzip the download, change to that directory, and enter the following commands in a terminal window, + + + +npm install # install node packages +ng add @angular/pwa # setup to use service worker +ng build # build the app for production; code is in the /dist folder + + + +The Angular development server (`ng serve`) doesn't support service worker applications. +The [`http-server package`](https://www.npmjs.com/package/http-server) from npm does. +You can run it without installing it like this: + + + +npx http-server -p 8080 -c-1 dist/ + + + +
+ ### Initial load -With the server running, point your browser at `http://localhost:8080`. +With the server running on port `8080`, point your browser at `http://localhost:8080`. Your application should load normally.
@@ -138,7 +166,7 @@ Make a change to the application, and watch the service worker install the updat 1. Close the application tab, but not the window. This should also close the Developer Tools. -1. Shut down `http-server`. +1. Shut down `http-server` (Ctrl-c). 1. Open `src/app/app.component.html` for editing. 1. Change the text `Welcome to {{title}}!` to `Bienvenue à {{title}}!`. 1. Build and run the server again: @@ -146,7 +174,7 @@ Make a change to the application, and watch the service worker install the updat ng build - http-server -p 8080 -c-1 dist/<project-name> + npx http-server -p 8080 -c-1 dist/ @@ -164,11 +192,21 @@ Now look at how the browser and service worker handle the updated application.
What went wrong? - Nothing, actually. + _Nothing, actually!_ + The Angular service worker is doing its job and serving the version of the application that it has **installed**, even though there is an update available. In the interest of speed, the service worker doesn't wait to check for updates before it serves the application that it has cached. +
+ Look at the `http-server` logs to see the service worker requesting `/ngsw.json`. + + + + [2023-09-07T00:37:24.372Z] "GET /ngsw.json?ngsw-cache-bust=0.9365263935102124" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36" + + + This is how the service worker checks for updates. 1. Refresh the page. @@ -185,8 +223,8 @@ Now look at how the browser and service worker handle the updated application. You might also be interested in the following: -* [App Shell](guide/app-shell) * [Communicating with service workers](guide/service-worker-communications) +* [App Shell](guide/app-shell) @@ -194,4 +232,4 @@ You might also be interested in the following: -@reviewed 2022-02-28 +@reviewed 2023-09-06 diff --git a/aio/content/guide/service-worker-intro.md b/aio/content/guide/service-worker-intro.md index b57b8872c8ecb..68b564a014e72 100644 --- a/aio/content/guide/service-worker-intro.md +++ b/aio/content/guide/service-worker-intro.md @@ -48,7 +48,7 @@ The file, called `ngsw.json` \(not to be confused with the [web app manifest](ht When an update to the application is deployed, the contents of the manifest change, informing the service worker that a new version of the application should be downloaded and cached. This manifest is generated from a CLI-generated configuration file called `ngsw-config.json`. -Installing the Angular service worker is as straightforward as including an `NgModule`. +Installing the Angular service worker is as straightforward as [running an Angular CLI command](guide/service-worker-getting-started#cli-command). In addition to registering the Angular service worker with the browser, this also makes a few services available for injection which interact with the service worker and can be used to control it. For example, an application can ask to be notified when a new update becomes available, or an application can ask the service worker to check the server for available updates. @@ -86,11 +86,11 @@ To learn more about other browsers that are service worker ready, see the [Can I The rest of the articles in this section specifically address the Angular implementation of service workers. -* [App Shell](guide/app-shell) * [Service Worker Communication](guide/service-worker-communications) * [Service Worker Notifications](guide/service-worker-notifications) * [Service Worker in Production](guide/service-worker-devops) * [Service Worker Configuration](guide/service-worker-config) +* [App Shell](guide/app-shell) For more information about service workers in general, see [Service Workers: an Introduction](https://developers.google.com/web/fundamentals/primers/service-workers). @@ -101,7 +101,7 @@ For additional recommendations and examples, see: * [Precaching with Angular Service Worker](https://web.dev/precaching-with-the-angular-service-worker) * [Creating a PWA with Angular CLI](https://web.dev/creating-pwa-with-angular-cli) -## Next steps +## Next step To begin using Angular service workers, see [Getting Started with service workers](guide/service-worker-getting-started). @@ -111,4 +111,4 @@ To begin using Angular service workers, see [Getting Started with service worker -@reviewed 2022-02-28 +@reviewed 2023-09-06 diff --git a/aio/content/navigation.json b/aio/content/navigation.json index 406f924c7e269..ab8cbbe26c9cc 100644 --- a/aio/content/navigation.json +++ b/aio/content/navigation.json @@ -700,11 +700,6 @@ "title": "Getting started", "tooltip": "Enabling the service worker in a CLI project and observing behavior in the browser." }, - { - "url": "guide/app-shell", - "title": "App shell", - "tooltip": "Render a portion of your app quickly to improve the startup experience." - }, { "url": "guide/service-worker-communications", "title": "Service worker communication", @@ -724,6 +719,11 @@ "url": "guide/service-worker-config", "title": "Service worker configuration", "tooltip": "Configuring service worker caching behavior." + }, + { + "url": "guide/app-shell", + "title": "App shell", + "tooltip": "Render a portion of your app quickly to improve the startup experience." } ] },