Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
4 changes: 4 additions & 0 deletions .prettierignore
Comment thread
josemontespg marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ renderers/lit/a2ui_explorer/src/generated/**
# Evaluation logs
eval/logs
**/eval/logs

# Untracked agent files
.lavish/
proxy.js
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