From 4e70c991845bedf9d1e14a40d06143df6a3bcf9f Mon Sep 17 00:00:00 2001 From: Jose Montes Date: Tue, 21 Jul 2026 20:31:50 +0000 Subject: [PATCH 1/5] Implement provideA2Ui helper for idiomatic Angular renderer configuration --- .../v0_9/core/a2ui-renderer.service.spec.ts | 18 ++++++++++++- .../src/v0_9/core/a2ui-renderer.service.ts | 25 ++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/renderers/angular/src/v0_9/core/a2ui-renderer.service.spec.ts b/renderers/angular/src/v0_9/core/a2ui-renderer.service.spec.ts index e2811bbb5d..f2cd1dbc53 100644 --- a/renderers/angular/src/v0_9/core/a2ui-renderer.service.spec.ts +++ b/renderers/angular/src/v0_9/core/a2ui-renderer.service.spec.ts @@ -15,7 +15,7 @@ */ import {TestBed} from '@angular/core/testing'; -import {A2uiRendererService, A2UI_RENDERER_CONFIG} from './a2ui-renderer.service'; +import {A2uiRendererService, A2UI_RENDERER_CONFIG, provideA2Ui} from './a2ui-renderer.service'; describe('A2uiRendererService', () => { let service: A2uiRendererService; @@ -84,3 +84,19 @@ describe('A2uiRendererService', () => { }); }); }); + +describe('provideA2Ui', () => { + it('should provide the configuration', () => { + const mockCatalog = { + components: new Map(), + functions: new Map(), + }; + TestBed.configureTestingModule({ + providers: [ + provideA2Ui({catalogs: [mockCatalog as any]}), + ], + }); + const config = TestBed.inject(A2UI_RENDERER_CONFIG); + expect(config).toEqual({catalogs: [mockCatalog as any]}); + }); +}); diff --git a/renderers/angular/src/v0_9/core/a2ui-renderer.service.ts b/renderers/angular/src/v0_9/core/a2ui-renderer.service.ts index 1e3d96c07c..732bfb6cb9 100644 --- a/renderers/angular/src/v0_9/core/a2ui-renderer.service.ts +++ b/renderers/angular/src/v0_9/core/a2ui-renderer.service.ts @@ -14,7 +14,15 @@ * limitations under the License. */ -import {Injectable, OnDestroy, InjectionToken, inject, EnvironmentInjector} from '@angular/core'; +import { + Injectable, + OnDestroy, + InjectionToken, + inject, + EnvironmentInjector, + EnvironmentProviders, + makeEnvironmentProviders, +} from '@angular/core'; import { MessageProcessor, SurfaceGroupModel, @@ -47,6 +55,21 @@ export const A2UI_RENDERER_CONFIG = new InjectionToken( 'A2UI_RENDERER_CONFIG', ); +/** + * Provides the A2UI renderer configuration. + * + * @param config The configuration for the A2UI renderer. + * @returns The providers for the A2UI renderer. + */ +export function provideA2Ui(config: RendererConfiguration): EnvironmentProviders { + return makeEnvironmentProviders([ + { + provide: A2UI_RENDERER_CONFIG, + useValue: config, + }, + ]); +} + /** * Manages A2UI v0.9 rendering sessions by bridging the MessageProcessor to Angular. * From 6902068cfdc5bdb815b11bea7429bbf4973bdafa Mon Sep 17 00:00:00 2001 From: Jose Montes Date: Tue, 21 Jul 2026 20:37:23 +0000 Subject: [PATCH 2/5] feat(angular): support factory configurations in provideA2Ui and update docs --- docs/public/guides/client-setup.md | 40 ++++++++++++++----- .../v0_9/core/a2ui-renderer.service.spec.ts | 22 +++++++++- .../src/v0_9/core/a2ui-renderer.service.ts | 10 +++-- .../projects/restaurant/src/app/app.config.ts | 21 ++++------ 4 files changed, 65 insertions(+), 28 deletions(-) diff --git a/docs/public/guides/client-setup.md b/docs/public/guides/client-setup.md index 3e9af292aa..d607c9e4d8 100644 --- a/docs/public/guides/client-setup.md +++ b/docs/public/guides/client-setup.md @@ -60,24 +60,42 @@ Once installed, you can use the renderer in your app. The Angular renderer provi ### Setup Example (v0.9) -A2UI uses versioned imports for its protocol-specific implementations. For v0.9, configure your application providers as follows: +A2UI uses versioned imports for its protocol-specific implementations. For v0.9, configure your application providers using `provideA2Ui` as follows: ```typescript import {ApplicationConfig} from '@angular/core'; -import {A2UI_RENDERER_CONFIG, A2uiRendererService, BasicCatalog} from '@a2ui/angular/v0_9'; +import {provideA2Ui, BasicCatalog} from '@a2ui/angular/v0_9'; export const appConfig: ApplicationConfig = { providers: [ - { - provide: A2UI_RENDERER_CONFIG, - useValue: { - catalogs: [new BasicCatalog()], - actionHandler: action => { - console.log('Action dispatched:', action); - }, + provideA2Ui({ + catalogs: [new BasicCatalog()], + actionHandler: action => { + console.log('Action dispatched:', action); }, - }, - A2uiRendererService, + }), + ], +}; +``` + +#### Dependency Injection in Action Handler + +If your `actionHandler` needs to inject dependencies (for example, to call a service when an action is dispatched), you can pass a factory function to `provideA2Ui`. Within this factory function, you can use Angular's `inject()` function: + +```typescript +import {ApplicationConfig, inject} from '@angular/core'; +import {provideA2Ui, BasicCatalog} from '@a2ui/angular/v0_9'; +import {MyActionDispatcherService} from './my-action-dispatcher.service'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideA2Ui(() => { + const dispatcher = inject(MyActionDispatcherService); + return { + catalogs: [new BasicCatalog()], + actionHandler: action => dispatcher.dispatch(action), + }; + }), ], }; ``` diff --git a/renderers/angular/src/v0_9/core/a2ui-renderer.service.spec.ts b/renderers/angular/src/v0_9/core/a2ui-renderer.service.spec.ts index f2cd1dbc53..e8251d9759 100644 --- a/renderers/angular/src/v0_9/core/a2ui-renderer.service.spec.ts +++ b/renderers/angular/src/v0_9/core/a2ui-renderer.service.spec.ts @@ -86,7 +86,7 @@ describe('A2uiRendererService', () => { }); describe('provideA2Ui', () => { - it('should provide the configuration', () => { + it('should provide the configuration and allow A2uiRendererService to be instantiated', () => { const mockCatalog = { components: new Map(), functions: new Map(), @@ -98,5 +98,25 @@ describe('provideA2Ui', () => { }); const config = TestBed.inject(A2UI_RENDERER_CONFIG); expect(config).toEqual({catalogs: [mockCatalog as any]}); + + const service = TestBed.inject(A2uiRendererService); + expect(service).toBeTruthy(); + }); + + it('should support providing the configuration via a factory function', () => { + const mockCatalog = { + components: new Map(), + functions: new Map(), + }; + TestBed.configureTestingModule({ + providers: [ + provideA2Ui(() => ({catalogs: [mockCatalog as any]})), + ], + }); + const config = TestBed.inject(A2UI_RENDERER_CONFIG); + expect(config).toEqual({catalogs: [mockCatalog as any]}); + + const service = TestBed.inject(A2uiRendererService); + expect(service).toBeTruthy(); }); }); diff --git a/renderers/angular/src/v0_9/core/a2ui-renderer.service.ts b/renderers/angular/src/v0_9/core/a2ui-renderer.service.ts index 732bfb6cb9..d4b0b2afde 100644 --- a/renderers/angular/src/v0_9/core/a2ui-renderer.service.ts +++ b/renderers/angular/src/v0_9/core/a2ui-renderer.service.ts @@ -58,14 +58,18 @@ export const A2UI_RENDERER_CONFIG = new InjectionToken( /** * Provides the A2UI renderer configuration. * - * @param config The configuration for the A2UI renderer. + * @param configOrFactory The configuration or a factory function that returns the configuration. * @returns The providers for the A2UI renderer. */ -export function provideA2Ui(config: RendererConfiguration): EnvironmentProviders { +export function provideA2Ui( + configOrFactory: RendererConfiguration | (() => RendererConfiguration), +): EnvironmentProviders { return makeEnvironmentProviders([ { provide: A2UI_RENDERER_CONFIG, - useValue: config, + ...(typeof configOrFactory === 'function' + ? {useFactory: configOrFactory} + : {useValue: configOrFactory}), }, ]); } diff --git a/samples/client/angular/projects/restaurant/src/app/app.config.ts b/samples/client/angular/projects/restaurant/src/app/app.config.ts index 0d3f746629..b6cbbcfe91 100644 --- a/samples/client/angular/projects/restaurant/src/app/app.config.ts +++ b/samples/client/angular/projects/restaurant/src/app/app.config.ts @@ -15,9 +15,8 @@ */ import { - A2uiRendererService, - A2UI_RENDERER_CONFIG, BasicCatalog, + provideA2Ui, provideMarkdownRenderer, } from '@a2ui/angular/v0_9'; import {Client} from './client'; @@ -37,17 +36,13 @@ export const appConfig: ApplicationConfig = { provideBrowserGlobalErrorListeners(), provideZonelessChangeDetection(), provideClientHydration(withEventReplay()), - { - provide: A2UI_RENDERER_CONFIG, - useFactory: () => { - const injector = inject(Injector); - return { - catalogs: [new BasicCatalog()], - actionHandler: (action: A2uiClientAction) => injector.get(Client).handleAction(action), - }; - }, - }, - A2uiRendererService, + provideA2Ui(() => { + const injector = inject(Injector); + return { + catalogs: [new BasicCatalog()], + actionHandler: (action: A2uiClientAction) => injector.get(Client).handleAction(action), + }; + }), provideMarkdownRenderer(renderMarkdown as any), { provide: IMAGE_CONFIG, From 140d1811b41ef9ef6ad9a9ecf45d04f731f4a17a Mon Sep 17 00:00:00 2001 From: Jose Montes Date: Tue, 21 Jul 2026 20:48:28 +0000 Subject: [PATCH 3/5] style: format code to fix presubmit lint/format-check --- .../angular/src/v0_9/core/a2ui-renderer.service.spec.ts | 8 ++------ .../angular/projects/restaurant/src/app/app.config.ts | 6 +----- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/renderers/angular/src/v0_9/core/a2ui-renderer.service.spec.ts b/renderers/angular/src/v0_9/core/a2ui-renderer.service.spec.ts index e8251d9759..af3ea08080 100644 --- a/renderers/angular/src/v0_9/core/a2ui-renderer.service.spec.ts +++ b/renderers/angular/src/v0_9/core/a2ui-renderer.service.spec.ts @@ -92,9 +92,7 @@ describe('provideA2Ui', () => { functions: new Map(), }; TestBed.configureTestingModule({ - providers: [ - provideA2Ui({catalogs: [mockCatalog as any]}), - ], + providers: [provideA2Ui({catalogs: [mockCatalog as any]})], }); const config = TestBed.inject(A2UI_RENDERER_CONFIG); expect(config).toEqual({catalogs: [mockCatalog as any]}); @@ -109,9 +107,7 @@ describe('provideA2Ui', () => { functions: new Map(), }; TestBed.configureTestingModule({ - providers: [ - provideA2Ui(() => ({catalogs: [mockCatalog as any]})), - ], + providers: [provideA2Ui(() => ({catalogs: [mockCatalog as any]}))], }); const config = TestBed.inject(A2UI_RENDERER_CONFIG); expect(config).toEqual({catalogs: [mockCatalog as any]}); diff --git a/samples/client/angular/projects/restaurant/src/app/app.config.ts b/samples/client/angular/projects/restaurant/src/app/app.config.ts index b6cbbcfe91..a3f0fc9779 100644 --- a/samples/client/angular/projects/restaurant/src/app/app.config.ts +++ b/samples/client/angular/projects/restaurant/src/app/app.config.ts @@ -14,11 +14,7 @@ * limitations under the License. */ -import { - BasicCatalog, - provideA2Ui, - provideMarkdownRenderer, -} from '@a2ui/angular/v0_9'; +import {BasicCatalog, provideA2Ui, provideMarkdownRenderer} from '@a2ui/angular/v0_9'; import {Client} from './client'; import {inject, Injector} from '@angular/core'; import {IMAGE_CONFIG} from '@angular/common'; From 5505bcc2bc72e52cf7fbf77f013434a219cfd763 Mon Sep 17 00:00:00 2001 From: Jose Montes Date: Tue, 21 Jul 2026 23:27:19 +0000 Subject: [PATCH 4/5] docs(angular): add changelog entry for provideA2Ui (#2061) Also ignore untracked agent files in prettier. --- .prettierignore | 4 ++++ renderers/angular/CHANGELOG.md | 1 + 2 files changed, 5 insertions(+) diff --git a/.prettierignore b/.prettierignore index 123cc95a69..e31c46f87e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -31,3 +31,7 @@ renderers/lit/a2ui_explorer/src/generated/** # Evaluation logs eval/logs **/eval/logs + +# Untracked agent files +.lavish/ +proxy.js diff --git a/renderers/angular/CHANGELOG.md b/renderers/angular/CHANGELOG.md index 34e04903be..7e2201117c 100644 --- a/renderers/angular/CHANGELOG.md +++ b/renderers/angular/CHANGELOG.md @@ -1,5 +1,6 @@ ## Unreleased +- Add standalone helper `provideA2Ui` configuration function. [#2061](https://github.com/a2ui-project/a2ui/pull/2061) - Standardize package entry points to `index.ts` while maintaining `public-api.ts` wrappers for backward compatibility. - (v0_8) Export `A2uiMessageSchema` in public API. From f637be00a855afd67e8cd69a2d5c85552cfe21e0 Mon Sep 17 00:00:00 2001 From: Jose Montes Date: Wed, 22 Jul 2026 19:07:33 +0000 Subject: [PATCH 5/5] Revert changes to .prettierignore --- .prettierignore | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.prettierignore b/.prettierignore index e31c46f87e..123cc95a69 100644 --- a/.prettierignore +++ b/.prettierignore @@ -31,7 +31,3 @@ renderers/lit/a2ui_explorer/src/generated/** # Evaluation logs eval/logs **/eval/logs - -# Untracked agent files -.lavish/ -proxy.js