Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 29 additions & 11 deletions docs/public/guides/client-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -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),
};
}),
],
};
```
Expand Down
1 change: 1 addition & 0 deletions renderers/angular/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.

Expand Down
34 changes: 33 additions & 1 deletion renderers/angular/src/v0_9/core/a2ui-renderer.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -84,3 +84,35 @@ describe('A2uiRendererService', () => {
});
});
});

describe('provideA2Ui', () => {
it('should provide the configuration and allow A2uiRendererService to be instantiated', () => {
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();
});

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();
});
});
29 changes: 28 additions & 1 deletion renderers/angular/src/v0_9/core/a2ui-renderer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -47,6 +55,25 @@ export const A2UI_RENDERER_CONFIG = new InjectionToken<RendererConfiguration>(
'A2UI_RENDERER_CONFIG',
);

/**
* Provides the A2UI renderer configuration.
*
* @param configOrFactory The configuration or a factory function that returns the configuration.
* @returns The providers for the A2UI renderer.
*/
export function provideA2Ui(
configOrFactory: RendererConfiguration | (() => RendererConfiguration),
): EnvironmentProviders {
return makeEnvironmentProviders([
{
provide: A2UI_RENDERER_CONFIG,
...(typeof configOrFactory === 'function'
? {useFactory: configOrFactory}
: {useValue: configOrFactory}),
},
]);
}

/**
* Manages A2UI v0.9 rendering sessions by bridging the MessageProcessor to Angular.
*
Expand Down
25 changes: 8 additions & 17 deletions samples/client/angular/projects/restaurant/src/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,7 @@
* limitations under the License.
*/

import {
A2uiRendererService,
A2UI_RENDERER_CONFIG,
BasicCatalog,
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';
Expand All @@ -37,17 +32,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,
Expand Down
Loading