From c905da12e2205c8a4bad6b77c0c813fe3eeeb073 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Fri, 26 Aug 2022 21:26:22 +0300 Subject: [PATCH] docs(service-worker): improve docs related to `SwUpdate` APIs (#46960) This commit improves documentation related to recently improved or deprecated `SwUpdate` APIs in the following ways: - Update [check-for-update.service.ts][1] to make use of the return value of [SwUpdate#checkForUpdate()][2]. - Update [prompt-update.service.ts][3] to not call [SwUpdate#activateUpdate()][4] and just reload the page. - Update the [SwUpdate#activateUpdate()][4] API docs to explain that it is only useful if you want to update a client without reloading and that it can easily lead to version skew. - Update [a code-snippet][5] to no longer be [hard-coded][6]. [1]: https://github.com/angular/angular/blob/9d9d05911dbd6e2f30e4c7bced0e41fd20ec4285/aio/content/examples/service-worker-getting-started/src/app/check-for-update.service.ts#L16 [2]: https://angular.io/api/service-worker/SwUpdate#checkForUpdate [3]: https://github.com/angular/angular/blob/9d9d05911dbd6e2f30e4c7bced0e41fd20ec4285/aio/content/examples/service-worker-getting-started/src/app/prompt-update.service.ts#L15 [4]: https://angular.io/api/service-worker/SwUpdate#activateUpdate [5]: https://github.com/angular/angular/blob/96c6139c9ab35aa6ab2330a5a79a5906d5c2e8be/packages/service-worker/src/update.ts#L44-L54 [6]: https://angular.io/guide/docs-style-guide#hard-coded-snippets Fixes #43665 PR Close #46960 --- .../src/app/check-for-update.service.ts | 9 ++++- .../src/app/prompt-update.service.ts | 40 ++++++++++++++----- .../guide/service-worker-communications.md | 14 ++++--- packages/service-worker/src/update.ts | 31 ++++++++------ 4 files changed, 66 insertions(+), 28 deletions(-) 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 86d09e80d0e..dab9b69a448 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 @@ -13,6 +13,13 @@ export class CheckForUpdateService { const everySixHours$ = interval(6 * 60 * 60 * 1000); const everySixHoursOnceAppIsStable$ = concat(appIsStable$, everySixHours$); - everySixHoursOnceAppIsStable$.subscribe(() => updates.checkForUpdate()); + everySixHoursOnceAppIsStable$.subscribe(async () => { + try { + const updateFound = await updates.checkForUpdate(); + console.log(updateFound ? 'A new version is available.' : 'Already on the latest version.'); + } catch (err) { + console.error('Failed to check for updates:', err); + } + }); } } 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 3d2ea87bae6..78eeac2bb6b 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 @@ -1,20 +1,40 @@ +// #docplaster import { Injectable } from '@angular/core'; -import { SwUpdate, UpdateAvailableEvent } from '@angular/service-worker'; +// #docregion sw-replicate-available + import { filter, map } from 'rxjs/operators'; +// #enddocregion sw-replicate-available +import { SwUpdate, VersionReadyEvent } from '@angular/service-worker'; -function promptUser(event: UpdateAvailableEvent): boolean { +function promptUser(event: VersionReadyEvent): boolean { return true; } -// #docregion sw-activate +// #docregion sw-version-ready @Injectable() export class PromptUpdateService { - constructor(updates: SwUpdate) { - updates.available.subscribe(event => { - if (promptUser(event)) { - updates.activateUpdate().then(() => document.location.reload()); - } - }); + constructor(swUpdate: SwUpdate) { + swUpdate.versionUpdates + .pipe(filter((evt): evt is VersionReadyEvent => evt.type === 'VERSION_READY')) + .subscribe(evt => { + if (promptUser(evt)) { + // Reload the page to update to the latest version. + document.location.reload(); + } + }); + // #enddocregion sw-version-ready + // #docregion sw-replicate-available + // ... + const updatesAvailable = swUpdate.versionUpdates.pipe( + filter((evt): evt is VersionReadyEvent => evt.type === 'VERSION_READY'), + map(evt => ({ + type: 'UPDATE_AVAILABLE', + current: evt.currentVersion, + available: evt.latestVersion, + }))); + // #enddocregion sw-replicate-available + // #docregion sw-version-ready } + } -// #enddocregion sw-activate +// #enddocregion sw-version-ready diff --git a/aio/content/guide/service-worker-communications.md b/aio/content/guide/service-worker-communications.md index a0023d30421..d5d4f9aa7d0 100644 --- a/aio/content/guide/service-worker-communications.md +++ b/aio/content/guide/service-worker-communications.md @@ -64,16 +64,20 @@ Alternatively, you might want to define a different [registration strategy](api/ -### Forcing update activation +### Updating to the latest version -If the current tab needs to be updated to the latest application version immediately, it can ask to do so with the `activateUpdate()` method: +You can update an existing tab to the latest version by reloading the page as soon as a new version is ready. +To avoid disrupting the user's progress, it is generally a good idea to prompt the user and let them confirm that it is OK to reload the page and update to the latest version: - +
-Calling `activateUpdate()` without reloading the page could break lazy-loading in a currently running app, especially if the lazy-loaded chunks use filenames with hashes, which change every version. -Therefore, it is recommended to reload the page once the promise returned by `activateUpdate()` is resolved. +Calling {@link SwUpdate#activateUpdate SwUpdate#activateUpdate()} updates a tab to the latest version without reloading the page, but this could break the application. + +Updating without reloading can create a version mismatch between the [application shell](guide/glossary#app-shell) and other page resources, such as [lazy-loaded chunks](guide/glossary#lazy-loading), whose filenames may change between versions. + +You should only use `activateUpdate()`, if you are certain it is safe for your specific use case.
diff --git a/packages/service-worker/src/update.ts b/packages/service-worker/src/update.ts index b9dece2b6dd..3b2523b3553 100644 --- a/packages/service-worker/src/update.ts +++ b/packages/service-worker/src/update.ts @@ -40,18 +40,11 @@ export class SwUpdate { * * @deprecated Use {@link versionUpdates} instead. * - * The of behavior `available` can be rebuild by filtering for the `VersionReadyEvent`: - * ``` - * import {filter, map} from 'rxjs/operators'; - * // ... - * const updatesAvailable = swUpdate.versionUpdates.pipe( - * filter((evt): evt is VersionReadyEvent => evt.type === 'VERSION_READY'), - * map(evt => ({ - * type: 'UPDATE_AVAILABLE', - * current: evt.currentVersion, - * available: evt.latestVersion, - * }))); - * ``` + * The behavior of `available` can be replicated by using `versionUpdates` by filtering for the + * `VersionReadyEvent`: + * + * {@example service-worker-getting-started/src/app/prompt-update.service.ts + * region='sw-replicate-available'} */ readonly available: Observable; @@ -124,6 +117,20 @@ export class SwUpdate { * Updates the current client (i.e. browser tab) to the latest version that is ready for * activation. * + * In most cases, you should not use this method and instead should update a client by reloading + * the page. + * + *
+ * + * Updating a client without reloading can easily result in a broken application due to a version + * mismatch between the [application shell](guide/glossary#app-shell) and other page resources, + * such as [lazy-loaded chunks](guide/glossary#lazy-loading), whose filenames may change between + * versions. + * + * Only use this method, if you are certain it is safe for your specific use case. + * + *
+ * * @returns a promise that * - resolves to `true` if an update was activated successfully * - resolves to `false` if no update was available (for example, the client was already on the