From ec9dc94ceeb3c026c64e01c6889b7f5c6fd25a66 Mon Sep 17 00:00:00 2001 From: Doug Parker Date: Thu, 6 Nov 2025 15:22:24 -0800 Subject: [PATCH] feat(platform-browser): add `context` to `createApplication` This is necessary to use SSR safely with `createApplication` and avoid constraining users to `bootstrapApplication`. It is one more step towards feature parity between `createApplication` and `bootstrapApplication`. --- .../public-api/platform-browser/index.api.md | 2 +- packages/platform-browser/src/browser.ts | 16 ++++++++++----- .../platform-server/test/integration_spec.ts | 20 +++++++++++++++++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/goldens/public-api/platform-browser/index.api.md b/goldens/public-api/platform-browser/index.api.md index c2d8ace6ce4..60adc31f9a2 100644 --- a/goldens/public-api/platform-browser/index.api.md +++ b/goldens/public-api/platform-browser/index.api.md @@ -52,7 +52,7 @@ export class By { } // @public -export function createApplication(options?: ApplicationConfig): Promise; +export function createApplication(options?: ApplicationConfig, context?: BootstrapContext): Promise; // @public export function disableDebugTools(): void; diff --git a/packages/platform-browser/src/browser.ts b/packages/platform-browser/src/browser.ts index 998ae5ab4fd..cd4913781f3 100644 --- a/packages/platform-browser/src/browser.ts +++ b/packages/platform-browser/src/browser.ts @@ -131,8 +131,7 @@ export async function bootstrapApplication( ): Promise { const config = { rootComponent, - platformRef: context?.platformRef, - ...createProvidersConfig(options), + ...createProvidersConfig(options, context), }; if ((typeof ngJitMode === 'undefined' || ngJitMode) && typeof fetch === 'function') { @@ -150,20 +149,27 @@ export async function bootstrapApplication( * * @param options Extra configuration for the application environment, see `ApplicationConfig` for * additional info. + * @param context Optional context object that can be used to provide a pre-existing + * platform injector. This is useful for advanced use-cases, for example, server-side + * rendering, where the platform is created for each request. * @returns A promise that returns an `ApplicationRef` instance once resolved. * * @publicApi */ -export async function createApplication(options?: ApplicationConfig): Promise { +export async function createApplication( + options?: ApplicationConfig, + context?: BootstrapContext, +): Promise { if ((typeof ngJitMode === 'undefined' || ngJitMode) && typeof fetch === 'function') { await resolveJitResources(); } - return internalCreateApplication(createProvidersConfig(options)); + return internalCreateApplication(createProvidersConfig(options, context)); } -function createProvidersConfig(options?: ApplicationConfig) { +function createProvidersConfig(options?: ApplicationConfig, context?: BootstrapContext) { return { + platformRef: context?.platformRef, appProviders: [...BROWSER_MODULE_PROVIDERS, ...(options?.providers ?? [])], platformProviders: INTERNAL_BROWSER_PLATFORM_PROVIDERS, }; diff --git a/packages/platform-server/test/integration_spec.ts b/packages/platform-server/test/integration_spec.ts index 6c22b030720..2e973d1a2ee 100644 --- a/packages/platform-server/test/integration_spec.ts +++ b/packages/platform-server/test/integration_spec.ts @@ -47,10 +47,12 @@ import { provideNgReflectAttributes, ɵSSR_CONTENT_INTEGRITY_MARKER as SSR_CONTENT_INTEGRITY_MARKER, provideZoneChangeDetection, + signal, } from '@angular/core'; import {TestBed} from '@angular/core/testing'; import { bootstrapApplication, + createApplication, BootstrapContext, BrowserModule, provideClientHydration, @@ -767,6 +769,24 @@ class HiddenModule {} TestBed.resetTestingModule(); }); + it('should render with `createApplication`', async () => { + const output = await renderApplication( + async (context) => { + const appRef = await createApplication( + { + providers: [provideZoneChangeDetection()], + }, + context, + ); + appRef.bootstrap(createMyAsyncServerApp(true)); + return appRef; + }, + {document: doc}, + ); + + expect(output).toBe(expectedOutput); + }); + it('using long form should work', async () => { const platform = platformServer([ {