mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(core): wait for HTTP in ngOnInit correctly before server render (#50573)
Previously, with `mergeMap` we did not cancel previous subscriptions to zoneIsStable which caused the application to be stablized before hand. Closes: #50562 PR Close #50573
This commit is contained in:
committed by
Alex Rickabaugh
parent
5cfb6cacdf
commit
c992109d6c
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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([]);
|
||||
});
|
||||
});
|
||||
@@ -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({
|
||||
|
||||
+27
@@ -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: ` <div class="one">{{ responseOne }}</div> `,
|
||||
})
|
||||
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<any>('http://localhost:4206/api').subscribe((response) => {
|
||||
this.responseOne = response.data;
|
||||
});
|
||||
}
|
||||
}
|
||||
+18
@@ -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 {}
|
||||
@@ -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
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
+29
@@ -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: ` <div class="one">{{ responseOne }}</div> `,
|
||||
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<any>('http://localhost:4206/api').subscribe((response) => {
|
||||
this.responseOne = response.data;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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<boolean> =
|
||||
inject(InitialRenderPendingTasks)
|
||||
.hasPendingTasks.pipe(
|
||||
mergeMap(hasPendingTasks => hasPendingTasks ? of(false) : this.zoneIsStable),
|
||||
switchMap(hasPendingTasks => hasPendingTasks ? of(false) : this.zoneIsStable),
|
||||
distinctUntilChanged(),
|
||||
share(),
|
||||
);
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user