diff --git a/packages/angular/ssr/src/utils/ng.ts b/packages/angular/ssr/src/utils/ng.ts index fe8148727425..34b2e8e36024 100644 --- a/packages/angular/ssr/src/utils/ng.ts +++ b/packages/angular/ssr/src/utils/ng.ts @@ -96,24 +96,26 @@ export async function renderAngular( applicationRef = await bootstrap({ platformRef }); } + const envInjector = applicationRef.injector; + const router = envInjector.get(Router); + const initialUrl = router.currentNavigation()?.initialUrl.toString(); + // Block until application is stable. await applicationRef.whenStable(); // TODO(alanagius): Find a way to avoid rendering here especially for redirects as any output will be discarded. - const envInjector = applicationRef.injector; const routerIsProvided = !!envInjector.get(ActivatedRoute, null); - const router = envInjector.get(Router); const lastSuccessfulNavigation = router.lastSuccessfulNavigation(); if (!routerIsProvided) { hasNavigationError = false; - } else if (lastSuccessfulNavigation?.finalUrl) { + } else if (lastSuccessfulNavigation?.finalUrl && initialUrl !== null) { hasNavigationError = false; - const { finalUrl, initialUrl } = lastSuccessfulNavigation; + const { finalUrl } = lastSuccessfulNavigation; const finalUrlStringified = finalUrl.toString(); - if (initialUrl.toString() !== finalUrlStringified) { + if (initialUrl !== finalUrlStringified) { const baseHref = envInjector.get(APP_BASE_HREF, null, { optional: true }) ?? envInjector.get(PlatformLocation).getBaseHrefFromDOM(); diff --git a/packages/angular/ssr/test/app-engine_spec.ts b/packages/angular/ssr/test/app-engine_spec.ts index 90c8d9ae10cc..966edadce843 100644 --- a/packages/angular/ssr/test/app-engine_spec.ts +++ b/packages/angular/ssr/test/app-engine_spec.ts @@ -19,18 +19,6 @@ import { RenderMode } from '../src/routes/route-config'; import { setAngularAppTestingManifest } from './testing-utils'; function createEntryPoint(locale: string) { - @Component({ - selector: `app-ssr-${locale}`, - template: `SSR works ${locale.toUpperCase()}`, - }) - class SSRComponent {} - - @Component({ - selector: `app-ssg-${locale}`, - template: `SSG works ${locale.toUpperCase()}`, - }) - class SSGComponent {} - return async () => { @Component({ selector: `app-home-${locale}`, diff --git a/packages/angular/ssr/test/app_spec.ts b/packages/angular/ssr/test/app_spec.ts index e5ccddbfddbf..f85d4700329f 100644 --- a/packages/angular/ssr/test/app_spec.ts +++ b/packages/angular/ssr/test/app_spec.ts @@ -11,7 +11,8 @@ import '@angular/compiler'; /* eslint-enable import/no-unassigned-import */ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; +import { CanActivateFn, Router } from '@angular/router'; import { AngularServerApp } from '../src/app'; import { RenderMode } from '../src/routes/route-config'; import { setAngularAppTestingManifest } from './testing-utils'; @@ -26,6 +27,31 @@ describe('AngularServerApp', () => { }) class HomeComponent {} + @Component({ + selector: 'app-redirect', + }) + class RedirectComponent { + constructor() { + void inject(Router).navigate([], { + queryParams: { filter: 'test' }, + }); + } + } + + const queryParamAdderGuard: CanActivateFn = (_route, state) => { + const urlTree = inject(Router).parseUrl(state.url); + + if (urlTree.queryParamMap.has('filter')) { + return true; + } + + urlTree.queryParams = { + filter: 'test', + }; + + return urlTree; + }; + setAngularAppTestingManifest( [ { path: 'home', component: HomeComponent }, @@ -33,7 +59,14 @@ describe('AngularServerApp', () => { { path: 'home-ssg', component: HomeComponent }, { path: 'page-with-headers', component: HomeComponent }, { path: 'page-with-status', component: HomeComponent }, + { path: 'redirect', redirectTo: 'home' }, + { path: 'redirect-via-navigate', component: RedirectComponent }, + { + path: 'redirect-via-guard', + canActivate: [queryParamAdderGuard], + component: HomeComponent, + }, { path: 'redirect/relative', redirectTo: 'home' }, { path: 'redirect/:param/relative', redirectTo: 'home' }, { path: 'redirect/absolute', redirectTo: '/home' }, @@ -259,11 +292,23 @@ describe('AngularServerApp', () => { }); describe('SSR pages', () => { - it('returns a 302 status and redirects to the correct location when redirectTo is a function', async () => { + it('returns a 302 status and redirects to the correct location when `redirectTo` is a function', async () => { const response = await app.handle(new Request('http://localhost/redirect-to-function')); expect(response?.headers.get('location')).toBe('/home'); expect(response?.status).toBe(302); }); + + it('returns a 302 status and redirects to the correct location when `router.navigate` is used', async () => { + const response = await app.handle(new Request('http://localhost/redirect-via-navigate')); + expect(response?.headers.get('location')).toBe('/redirect-via-navigate?filter=test'); + expect(response?.status).toBe(302); + }); + + it('returns a 302 status and redirects to the correct location when `urlTree` is updated in a guard', async () => { + const response = await app.handle(new Request('http://localhost/redirect-via-guard')); + expect(response?.headers.get('location')).toBe('/redirect-via-guard?filter=test'); + expect(response?.status).toBe(302); + }); }); }); });