diff --git a/aio/content/guide/service-worker-config.md b/aio/content/guide/service-worker-config.md
index 1cb77cfb56..c2273b28ca 100644
--- a/aio/content/guide/service-worker-config.md
+++ b/aio/content/guide/service-worker-config.md
@@ -218,15 +218,3 @@ If the field is omitted, it defaults to:
'!/**/*__*/**', // Exclude URLs containing `__` in any other segment.
]
```
-
-## `register options`
-
-
- You can pass some options to the `register()` method.
-- enabled: optional parameter, by default is true, if enabled is false, the module will behave like the browser not support service worker, and service worker will not be registered.
-- scope: optional parameter, to specify the subset of your content that you want the service worker to control.
-- registrationStrategy: optional parameter, specify a strategy that determines when to register the service worker, the available options are:
- - registerWhenStable: this is the default behavior, the service worker will register when the application is stable (no microTasks or macroTasks remain).
- - registerImmediately: register immediately without waiting the application to become stable.
- - registerDelay:timeout : register after the timeout period, `timeout` is the number of milliseconds to delay registration. For example `registerDelay:5000` would register the service worker after 5 seconds. If the number of `timeout` is not given (`registerDelay`), by default, `timeout` will be `0`, but it is not equal to `registerImmediately`, it will still run a `setTimeout(register, 0)` to wait all `microTasks` to finish then perform registration of the service worker.
- - A factory to get Observable : you can also specify a factory which returns an Observable, the service worker will be registered the first time that a value is emitted by the Observable.
\ No newline at end of file
diff --git a/packages/service-worker/src/module.ts b/packages/service-worker/src/module.ts
index 5d07f9e88e..39e8f62e65 100644
--- a/packages/service-worker/src/module.ts
+++ b/packages/service-worker/src/module.ts
@@ -8,8 +8,8 @@
import {isPlatformBrowser} from '@angular/common';
import {APP_INITIALIZER, ApplicationRef, InjectionToken, Injector, ModuleWithProviders, NgModule, PLATFORM_ID} from '@angular/core';
-import {Observable} from 'rxjs';
-import {filter, take} from 'rxjs/operators';
+import {Observable, of } from 'rxjs';
+import {delay, filter, take} from 'rxjs/operators';
import {NgswCommChannel} from './low_level';
import {SwPush} from './push';
@@ -43,7 +43,32 @@ export abstract class SwRegistrationOptions {
*/
scope?: string;
- registrationStrategy?: (() => Observable)|string;
+ /**
+ * Defines the ServiceWorker registration strategy, which determines when it will be registered
+ * with the browser.
+ *
+ * The default behavior of registering once the application stabilizes (i.e. as soon as there are
+ * no pending micro- and macro-tasks), is designed register the ServiceWorker as soon as possible
+ * but without affecting the application's first time load.
+ *
+ * Still, there might be cases where you want more control over when the ServiceWorker is
+ * registered (e.g. there might be a long-running timeout or polling interval, preventing the app
+ * to stabilize). The available option are:
+ *
+ * - `registerWhenStable`: Register as soon as the application stabilizes (no pending
+ * micro-/macro-tasks).
+ * - `registerImmediately`: Register immediately.
+ * - `registerWithDelay:`: Register with a delay of `` milliseconds. For
+ * example, use `registerWithDelay:5000` to register the ServiceWorker after 5 seconds. If
+ * `` is omitted, is defaults to `0`, which will register the ServiceWorker as soon
+ * as possible but still asynchronously, once all pending micro-tasks are completed.
+ * - An [Observable](guide/observables) factory function: A function that returns an `Observable`.
+ * The function will be used at runtime to obtain and subscribe to the `Observable` and the
+ * ServiceWorker will be registered as soon as the first value is emitted.
+ *
+ * Default: 'registerWhenStable'
+ */
+ registrationStrategy?: string|(() => Observable);
}
export const SCRIPT = new InjectionToken('NGSW_REGISTER_SCRIPT');
@@ -52,13 +77,10 @@ export function ngswAppInitializer(
injector: Injector, script: string, options: SwRegistrationOptions,
platformId: string): Function {
const initializer = () => {
- const app = injector.get(ApplicationRef);
if (!(isPlatformBrowser(platformId) && ('serviceWorker' in navigator) &&
options.enabled !== false)) {
return;
}
- const whenStable =
- app.isStable.pipe(filter((stable: boolean) => !!stable), take(1)).toPromise();
// Wait for service worker controller changes, and fire an INITIALIZE action when a new SW
// becomes active. This allows the SW to initialize itself even if there is no application
@@ -69,35 +91,34 @@ export function ngswAppInitializer(
}
});
- // Don't return the Promise, as that will block the application until the SW is registered, and
- // cause a crash if the SW registration fails.
+ let readyToRegister$: Observable;
+
if (typeof options.registrationStrategy === 'function') {
- const observable = options.registrationStrategy();
- const subscription = observable.subscribe(() => {
- navigator.serviceWorker.register(script, {scope: options.scope});
- subscription.unsubscribe();
- });
+ readyToRegister$ = options.registrationStrategy();
} else {
- const registrationStrategy = typeof options.registrationStrategy === 'string' ?
- options.registrationStrategy :
- 'registerWhenStable';
- if (registrationStrategy === 'registerWhenStable') {
- whenStable.then(() => navigator.serviceWorker.register(script, {scope: options.scope}));
- } else if (registrationStrategy === 'registerImmediately') {
- navigator.serviceWorker.register(script, {scope: options.scope});
- } else if (registrationStrategy.indexOf('registerDelay') !== -1) {
- const split = registrationStrategy.split(':');
- const delayStr = split.length > 1 ? split[1] : undefined;
- const delay = Number(delayStr);
- setTimeout(
- () => navigator.serviceWorker.register(script, {scope: options.scope}),
- typeof delay === 'number' ? delay : 0);
- } else {
- // wrong strategy
- throw new Error(
- `Unknown service worker registration strategy: ${options.registrationStrategy}`);
+ const [strategy, ...args] = (options.registrationStrategy || 'registerWhenStable').split(':');
+ switch (strategy) {
+ case 'registerImmediately':
+ readyToRegister$ = of (null);
+ break;
+ case 'registerWithDelay':
+ readyToRegister$ = of (null).pipe(delay(+args[0] || 0));
+ break;
+ case 'registerWhenStable':
+ const appRef = injector.get(ApplicationRef);
+ readyToRegister$ = appRef.isStable.pipe(filter(stable => stable));
+ break;
+ default:
+ // Unknown strategy.
+ throw new Error(
+ `Unknown ServiceWorker registration strategy: ${options.registrationStrategy}`);
}
}
+
+ // Don't return anything to avoid blocking the application until the SW is registered or
+ // causing a crash if the SW registration fails.
+ readyToRegister$.pipe(take(1)).subscribe(
+ () => navigator.serviceWorker.register(script, {scope: options.scope}));
};
return initializer;
}
diff --git a/tools/public_api_guard/service-worker/service-worker.d.ts b/tools/public_api_guard/service-worker/service-worker.d.ts
index 1ecb0bf761..06a389a790 100644
--- a/tools/public_api_guard/service-worker/service-worker.d.ts
+++ b/tools/public_api_guard/service-worker/service-worker.d.ts
@@ -21,7 +21,7 @@ export declare class SwPush {
export declare abstract class SwRegistrationOptions {
enabled?: boolean;
- registrationStrategy?: (() => Observable) | string;
+ registrationStrategy?: string | (() => Observable);
scope?: string;
}