diff --git a/goldens/size-tracking/integration-payloads.json b/goldens/size-tracking/integration-payloads.json
index 6f517e3ba56..e6952dd0d6a 100644
--- a/goldens/size-tracking/integration-payloads.json
+++ b/goldens/size-tracking/integration-payloads.json
@@ -2,7 +2,7 @@
"cli-hello-world": {
"uncompressed": {
"runtime": 908,
- "main": 129295,
+ "main": 134468,
"polyfills": 33792
}
},
@@ -24,14 +24,14 @@
"forms": {
"uncompressed": {
"runtime": 888,
- "main": 160778,
+ "main": 166256,
"polyfills": 33772
}
},
"animations": {
"uncompressed": {
"runtime": 898,
- "main": 159461,
+ "main": 164757,
"polyfills": 33782
}
},
@@ -56,4 +56,4 @@
"polyfills": 33802
}
}
-}
\ No newline at end of file
+}
diff --git a/integration/platform-server/e2e/src/http-transferstate-lazy-on-init-spec.ts b/integration/platform-server/e2e/src/http-transferstate-lazy-on-init-spec.ts
new file mode 100644
index 00000000000..af7081933d9
--- /dev/null
+++ b/integration/platform-server/e2e/src/http-transferstate-lazy-on-init-spec.ts
@@ -0,0 +1,44 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {browser, by, element} from 'protractor';
+import {bootstrapClientApp, navigateTo, verifyNoBrowserErrors} from './util';
+
+describe('Http TransferState Lazy On Init', () => {
+ beforeEach(async () => {
+ // Don't wait for Angular since it is not bootstrapped automatically.
+ await browser.waitForAngularEnabled(false);
+
+ // Load the page without waiting for Angular since it is not bootstrapped automatically.
+ await navigateTo('http-transferstate-lazy-on-init');
+ });
+
+ afterEach(async () => {
+ // Make sure there were no client side errors.
+ await verifyNoBrowserErrors();
+ });
+
+ it('should transfer http state in lazy component', async () => {
+ // Test the contents from the server.
+ expect(await element(by.css('div.one')).getText()).toBe('API 1 response');
+
+ // Bootstrap the client side app and retest the contents
+ await bootstrapClientApp();
+ expect(await element(by.css('div.one')).getText()).toBe('API 1 response');
+
+ // Validate that there were no HTTP calls to '/api'.
+ const requests = await browser.executeScript(() => {
+ return performance.getEntriesByType('resource');
+ });
+ const apiRequests = (requests as {name: string}[])
+ .filter(({name}) => name.includes('/api'))
+ .map(({name}) => name);
+
+ expect(apiRequests).toEqual([]);
+ });
+});
diff --git a/integration/platform-server/projects/ngmodule/src/app/app-routing.module.ts b/integration/platform-server/projects/ngmodule/src/app/app-routing.module.ts
index 701e7bc35a7..15b321b453c 100644
--- a/integration/platform-server/projects/ngmodule/src/app/app-routing.module.ts
+++ b/integration/platform-server/projects/ngmodule/src/app/app-routing.module.ts
@@ -19,6 +19,13 @@ const routes: Routes = [
(m) => m.HttpTransferStateModule
),
},
+ {
+ path: 'http-transferstate-lazy-on-init',
+ loadChildren: () =>
+ import('./http-transferstate-lazy-on-init/http-transferstate-lazy-on-init.module').then(
+ (m) => m.HttpTransferStateOnInitModule
+ ),
+ },
];
@NgModule({
diff --git a/integration/platform-server/projects/ngmodule/src/app/http-transferstate-lazy-on-init/http-transferstate-lazy-on-init.component.ts b/integration/platform-server/projects/ngmodule/src/app/http-transferstate-lazy-on-init/http-transferstate-lazy-on-init.component.ts
new file mode 100644
index 00000000000..be88110e919
--- /dev/null
+++ b/integration/platform-server/projects/ngmodule/src/app/http-transferstate-lazy-on-init/http-transferstate-lazy-on-init.component.ts
@@ -0,0 +1,27 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {HttpClient} from '@angular/common/http';
+import {Component, OnInit} from '@angular/core';
+
+@Component({
+ selector: 'transfer-state-http-on-init',
+ template: `
{{ responseOne }}
`,
+})
+export class TransferStateComponentOnInit implements OnInit {
+ responseOne: string = '';
+
+ constructor(private readonly httpClient: HttpClient) {}
+
+ ngOnInit(): void {
+ // Test that HTTP cache works when HTTP call is made in a lifecycle hook.
+ this.httpClient.get('http://localhost:4206/api').subscribe((response) => {
+ this.responseOne = response.data;
+ });
+ }
+}
diff --git a/integration/platform-server/projects/ngmodule/src/app/http-transferstate-lazy-on-init/http-transferstate-lazy-on-init.module.ts b/integration/platform-server/projects/ngmodule/src/app/http-transferstate-lazy-on-init/http-transferstate-lazy-on-init.module.ts
new file mode 100644
index 00000000000..46fa2fd3ca0
--- /dev/null
+++ b/integration/platform-server/projects/ngmodule/src/app/http-transferstate-lazy-on-init/http-transferstate-lazy-on-init.module.ts
@@ -0,0 +1,18 @@
+import {CommonModule} from '@angular/common';
+import {HttpClientModule} from '@angular/common/http';
+import {NgModule} from '@angular/core';
+import {RouterModule, Routes} from '@angular/router';
+import {TransferStateComponentOnInit} from './http-transferstate-lazy-on-init.component';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: TransferStateComponentOnInit,
+ },
+];
+
+@NgModule({
+ imports: [RouterModule.forChild(routes), HttpClientModule, CommonModule],
+ declarations: [TransferStateComponentOnInit],
+})
+export class HttpTransferStateOnInitModule {}
diff --git a/integration/platform-server/projects/standalone/src/app/app.routes.ts b/integration/platform-server/projects/standalone/src/app/app.routes.ts
index 851ce59d373..697c7ecb730 100644
--- a/integration/platform-server/projects/standalone/src/app/app.routes.ts
+++ b/integration/platform-server/projects/standalone/src/app/app.routes.ts
@@ -18,4 +18,11 @@ export const routes: Routes = [
(c) => c.TransferStateComponent
),
},
+ {
+ path: 'http-transferstate-lazy-on-init',
+ loadComponent: () =>
+ import('./http-transferstate-lazy-on-init/http-transfer-state-on-init.component').then(
+ (c) => c.TransferStateOnInitComponent
+ ),
+ },
];
diff --git a/integration/platform-server/projects/standalone/src/app/http-transferstate-lazy-on-init/http-transfer-state-on-init.component.ts b/integration/platform-server/projects/standalone/src/app/http-transferstate-lazy-on-init/http-transfer-state-on-init.component.ts
new file mode 100644
index 00000000000..6801b8c7708
--- /dev/null
+++ b/integration/platform-server/projects/standalone/src/app/http-transferstate-lazy-on-init/http-transfer-state-on-init.component.ts
@@ -0,0 +1,29 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {HttpClient} from '@angular/common/http';
+import {Component, OnInit} from '@angular/core';
+
+@Component({
+ selector: 'transfer-state-http',
+ standalone: true,
+ template: ` {{ responseOne }}
`,
+ providers: [HttpClient],
+})
+export class TransferStateOnInitComponent implements OnInit {
+ responseOne: string = '';
+
+ constructor(private readonly httpClient: HttpClient) {}
+
+ ngOnInit(): void {
+ // Test that HTTP cache works when HTTP call is made in a lifecycle hook.
+ this.httpClient.get('http://localhost:4206/api').subscribe((response) => {
+ this.responseOne = response.data;
+ });
+ }
+}
diff --git a/packages/core/src/application_ref.ts b/packages/core/src/application_ref.ts
index ec9b181f648..7ee09d94ac6 100644
--- a/packages/core/src/application_ref.ts
+++ b/packages/core/src/application_ref.ts
@@ -9,7 +9,7 @@
import './util/ng_jit_mode';
import {Observable, of, Subscription} from 'rxjs';
-import {distinctUntilChanged, mergeMap, share} from 'rxjs/operators';
+import {distinctUntilChanged, share, switchMap} from 'rxjs/operators';
import {ApplicationInitStatus} from './application_init';
import {PLATFORM_INITIALIZER} from './application_tokens';
@@ -847,7 +847,7 @@ export class ApplicationRef {
public readonly isStable: Observable =
inject(InitialRenderPendingTasks)
.hasPendingTasks.pipe(
- mergeMap(hasPendingTasks => hasPendingTasks ? of(false) : this.zoneIsStable),
+ switchMap(hasPendingTasks => hasPendingTasks ? of(false) : this.zoneIsStable),
distinctUntilChanged(),
share(),
);
diff --git a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json
index 806146da292..2fa35c77d53 100644
--- a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json
@@ -500,6 +500,12 @@
{
"name": "Subscription"
},
+ {
+ "name": "SwitchMapOperator"
+ },
+ {
+ "name": "SwitchMapSubscriber"
+ },
{
"name": "TESTABILITY"
},
@@ -851,6 +857,9 @@
{
"name": "forwardRef"
},
+ {
+ "name": "from"
+ },
{
"name": "fromArray"
},
@@ -1034,6 +1043,9 @@
{
"name": "injectableDefOrInjectorDefFactory"
},
+ {
+ "name": "innerSubscribe"
+ },
{
"name": "insertBloom"
},
@@ -1175,6 +1187,9 @@
{
"name": "makeTimingAst"
},
+ {
+ "name": "map"
+ },
{
"name": "markAsComponentHost"
},
@@ -1187,6 +1202,9 @@
{
"name": "maybeWrapInNotSelector"
},
+ {
+ "name": "mergeAll"
+ },
{
"name": "mergeHostAttribute"
},
@@ -1415,6 +1433,9 @@
{
"name": "subscribeToArray"
},
+ {
+ "name": "switchMap"
+ },
{
"name": "throwProviderNotFoundError"
},
diff --git a/packages/core/test/bundling/animations/bundle.golden_symbols.json b/packages/core/test/bundling/animations/bundle.golden_symbols.json
index 11f9c76ae7d..47f7c9f0ad5 100644
--- a/packages/core/test/bundling/animations/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/animations/bundle.golden_symbols.json
@@ -542,6 +542,12 @@
{
"name": "Subscription"
},
+ {
+ "name": "SwitchMapOperator"
+ },
+ {
+ "name": "SwitchMapSubscriber"
+ },
{
"name": "TESTABILITY"
},
@@ -914,6 +920,9 @@
{
"name": "forwardRef"
},
+ {
+ "name": "from"
+ },
{
"name": "fromArray"
},
@@ -1100,6 +1109,9 @@
{
"name": "injectableDefOrInjectorDefFactory"
},
+ {
+ "name": "innerSubscribe"
+ },
{
"name": "insertBloom"
},
@@ -1241,6 +1253,9 @@
{
"name": "makeTimingAst"
},
+ {
+ "name": "map"
+ },
{
"name": "markAsComponentHost"
},
@@ -1253,6 +1268,9 @@
{
"name": "maybeWrapInNotSelector"
},
+ {
+ "name": "mergeAll"
+ },
{
"name": "mergeHostAttribute"
},
@@ -1490,6 +1508,9 @@
{
"name": "subscribeToArray"
},
+ {
+ "name": "switchMap"
+ },
{
"name": "throwProviderNotFoundError"
},
diff --git a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json
index 3fe3ae56328..c0ec83f40c1 100644
--- a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json
@@ -407,6 +407,12 @@
{
"name": "Subscription"
},
+ {
+ "name": "SwitchMapOperator"
+ },
+ {
+ "name": "SwitchMapSubscriber"
+ },
{
"name": "TESTABILITY"
},
@@ -689,6 +695,9 @@
{
"name": "forwardRef"
},
+ {
+ "name": "from"
+ },
{
"name": "fromArray"
},
@@ -866,6 +875,9 @@
{
"name": "injectableDefOrInjectorDefFactory"
},
+ {
+ "name": "innerSubscribe"
+ },
{
"name": "insertBloom"
},
@@ -980,6 +992,9 @@
{
"name": "makeRecord"
},
+ {
+ "name": "map"
+ },
{
"name": "markAsComponentHost"
},
@@ -992,6 +1007,9 @@
{
"name": "maybeWrapInNotSelector"
},
+ {
+ "name": "mergeAll"
+ },
{
"name": "mergeHostAttribute"
},
@@ -1181,6 +1199,9 @@
{
"name": "subscribeToArray"
},
+ {
+ "name": "switchMap"
+ },
{
"name": "throwProviderNotFoundError"
},
diff --git a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json
index 2230f1ecee7..ebc2528263d 100644
--- a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json
@@ -548,6 +548,12 @@
{
"name": "Subscription"
},
+ {
+ "name": "SwitchMapOperator"
+ },
+ {
+ "name": "SwitchMapSubscriber"
+ },
{
"name": "TESTABILITY"
},
@@ -1199,6 +1205,9 @@
{
"name": "injectableDefOrInjectorDefFactory"
},
+ {
+ "name": "innerSubscribe"
+ },
{
"name": "insertBloom"
},
@@ -1379,6 +1388,9 @@
{
"name": "maybeWrapInNotSelector"
},
+ {
+ "name": "mergeAll"
+ },
{
"name": "mergeErrors"
},
@@ -1646,6 +1658,9 @@
{
"name": "subscribeToArray"
},
+ {
+ "name": "switchMap"
+ },
{
"name": "throwProviderNotFoundError"
},
diff --git a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json
index 749d251eb7f..d0e0a387bb1 100644
--- a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json
@@ -536,6 +536,12 @@
{
"name": "Subscription"
},
+ {
+ "name": "SwitchMapOperator"
+ },
+ {
+ "name": "SwitchMapSubscriber"
+ },
{
"name": "TESTABILITY"
},
@@ -1163,6 +1169,9 @@
{
"name": "injectableDefOrInjectorDefFactory"
},
+ {
+ "name": "innerSubscribe"
+ },
{
"name": "insertBloom"
},
@@ -1337,6 +1346,9 @@
{
"name": "maybeWrapInNotSelector"
},
+ {
+ "name": "mergeAll"
+ },
{
"name": "mergeErrors"
},
@@ -1622,6 +1634,9 @@
{
"name": "subscribeToArray"
},
+ {
+ "name": "switchMap"
+ },
{
"name": "throwProviderNotFoundError"
},
diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json
index ed1d8f45fb9..c25234c0e57 100644
--- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json
@@ -314,6 +314,12 @@
{
"name": "Subscription"
},
+ {
+ "name": "SwitchMapOperator"
+ },
+ {
+ "name": "SwitchMapSubscriber"
+ },
{
"name": "TESTABILITY"
},
@@ -536,6 +542,9 @@
{
"name": "forwardRef"
},
+ {
+ "name": "from"
+ },
{
"name": "fromArray"
},
@@ -686,6 +695,9 @@
{
"name": "injectableDefOrInjectorDefFactory"
},
+ {
+ "name": "innerSubscribe"
+ },
{
"name": "insertBloom"
},
@@ -767,6 +779,9 @@
{
"name": "makeRecord"
},
+ {
+ "name": "map"
+ },
{
"name": "markViewDirty"
},
@@ -776,6 +791,9 @@
{
"name": "maybeWrapInNotSelector"
},
+ {
+ "name": "mergeAll"
+ },
{
"name": "mergeHostAttribute"
},
@@ -929,6 +947,9 @@
{
"name": "subscribeToArray"
},
+ {
+ "name": "switchMap"
+ },
{
"name": "throwProviderNotFoundError"
},
diff --git a/packages/core/test/bundling/hydration/bundle.golden_symbols.json b/packages/core/test/bundling/hydration/bundle.golden_symbols.json
index 6cede02c989..9b6f035cba0 100644
--- a/packages/core/test/bundling/hydration/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/hydration/bundle.golden_symbols.json
@@ -458,6 +458,12 @@
{
"name": "Subscription"
},
+ {
+ "name": "SwitchMapOperator"
+ },
+ {
+ "name": "SwitchMapSubscriber"
+ },
{
"name": "TESTABILITY"
},
@@ -746,6 +752,9 @@
{
"name": "forwardRef"
},
+ {
+ "name": "from"
+ },
{
"name": "fromArray"
},
@@ -926,6 +935,9 @@
{
"name": "injectableDefOrInjectorDefFactory"
},
+ {
+ "name": "innerSubscribe"
+ },
{
"name": "insertBloom"
},
@@ -1046,6 +1058,9 @@
{
"name": "makeRecord"
},
+ {
+ "name": "map"
+ },
{
"name": "markViewDirty"
},
@@ -1055,6 +1070,9 @@
{
"name": "maybeWrapInNotSelector"
},
+ {
+ "name": "mergeAll"
+ },
{
"name": "mergeHostAttribute"
},
@@ -1241,6 +1259,9 @@
{
"name": "subscribeToArray"
},
+ {
+ "name": "switchMap"
+ },
{
"name": "throwIfEmpty"
},
diff --git a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json
index 03dd4c90869..a967b543010 100644
--- a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json
@@ -374,6 +374,12 @@
{
"name": "Subscription"
},
+ {
+ "name": "SwitchMapOperator"
+ },
+ {
+ "name": "SwitchMapSubscriber"
+ },
{
"name": "TESTABILITY"
},
@@ -614,6 +620,9 @@
{
"name": "forwardRef"
},
+ {
+ "name": "from"
+ },
{
"name": "fromArray"
},
@@ -770,6 +779,9 @@
{
"name": "injectableDefOrInjectorDefFactory"
},
+ {
+ "name": "innerSubscribe"
+ },
{
"name": "insertBloom"
},
@@ -860,6 +872,9 @@
{
"name": "makeRecord"
},
+ {
+ "name": "map"
+ },
{
"name": "markViewDirty"
},
@@ -869,6 +884,9 @@
{
"name": "maybeWrapInNotSelector"
},
+ {
+ "name": "mergeAll"
+ },
{
"name": "mergeHostAttribute"
},
@@ -1031,6 +1049,9 @@
{
"name": "subscribeToArray"
},
+ {
+ "name": "switchMap"
+ },
{
"name": "throwProviderNotFoundError"
},
diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json
index e290dc33458..86a119e9294 100644
--- a/packages/core/test/bundling/todo/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json
@@ -437,6 +437,12 @@
{
"name": "Subscription"
},
+ {
+ "name": "SwitchMapOperator"
+ },
+ {
+ "name": "SwitchMapSubscriber"
+ },
{
"name": "TESTABILITY"
},
@@ -812,6 +818,9 @@
{
"name": "forwardRef"
},
+ {
+ "name": "from"
+ },
{
"name": "fromArray"
},
@@ -1037,6 +1046,9 @@
{
"name": "injectableDefOrInjectorDefFactory"
},
+ {
+ "name": "innerSubscribe"
+ },
{
"name": "insertBloom"
},
@@ -1175,6 +1187,9 @@
{
"name": "makeRecord"
},
+ {
+ "name": "map"
+ },
{
"name": "markAsComponentHost"
},
@@ -1190,6 +1205,9 @@
{
"name": "maybeWrapInNotSelector"
},
+ {
+ "name": "mergeAll"
+ },
{
"name": "mergeHostAttribute"
},
@@ -1406,6 +1424,9 @@
{
"name": "subscribeToArray"
},
+ {
+ "name": "switchMap"
+ },
{
"name": "throwProviderNotFoundError"
},