diff --git a/angular/src/app/app.component.spec.ts b/angular/src/app/app.component.spec.ts index c57870fa0..e84745d01 100644 --- a/angular/src/app/app.component.spec.ts +++ b/angular/src/app/app.component.spec.ts @@ -1,21 +1,18 @@ import { TestBed } from "@angular/core/testing"; import { AppComponent } from "./app.component"; import { FormsModule } from "@angular/forms"; -import { ElizaProvider } from "src/connect/client.provider"; +import { provideConnect } from "src/connect/connect.module"; import { ELIZA } from "src/connect/tokens"; -import { ConnectModule } from "src/connect/connect.module"; describe("AppComponent", () => { beforeEach(async () => { await TestBed.configureTestingModule({ - providers: [ElizaProvider], - imports: [ - AppComponent, - FormsModule, - ConnectModule.forRoot({ + providers: [ + provideConnect({ baseUrl: "https://demo.connectrpc.com", }), ], + imports: [AppComponent, FormsModule], }).compileComponents(); }); diff --git a/angular/src/app/app.component.ts b/angular/src/app/app.component.ts index 8b8362f2a..2f8df9bd4 100644 --- a/angular/src/app/app.component.ts +++ b/angular/src/app/app.component.ts @@ -1,8 +1,6 @@ -import { Component, Inject } from "@angular/core"; +import { Component, inject } from "@angular/core"; import { CommonModule } from "@angular/common"; import { FormsModule } from "@angular/forms"; -import { ObservableClient } from "src/connect/observable-client"; -import { ElizaService } from "src/gen/connectrpc/eliza/v1/eliza_pb"; import { ELIZA } from "../connect/tokens"; interface Response { @@ -10,15 +8,14 @@ interface Response { sender: "eliza" | "user"; } -// Note that with Angular v19, standalone components are the default so no -// need for standalone: true here. @Component({ - imports: [CommonModule, FormsModule], selector: "app-root", - templateUrl: "./app.component.html", + imports: [CommonModule, FormsModule], styleUrls: ["./app.component.css"], + templateUrl: "./app.component.html", }) export class AppComponent { + client = inject(ELIZA); title = "Eliza"; project = "Angular"; statement: string = ""; @@ -28,12 +25,7 @@ export class AppComponent { sender: "eliza", }, ]; - introFinished: boolean = false; - - constructor( - @Inject(ELIZA) - private client: ObservableClient, - ) {} + introFinished = false; onSend(event?: MouseEvent) { if (event) { diff --git a/angular/src/app/app.config.ts b/angular/src/app/app.config.ts new file mode 100644 index 000000000..04beb6828 --- /dev/null +++ b/angular/src/app/app.config.ts @@ -0,0 +1,12 @@ +import { ApplicationConfig } from '@angular/core'; +import { provideConnect } from "../connect/connect.module"; +import { provideProtractorTestingSupport } from "@angular/platform-browser"; + +export const appConfig: ApplicationConfig = { + providers: [ + provideConnect({ + baseUrl: "https://demo.connectrpc.com", + }), + provideProtractorTestingSupport(), + ], +}; diff --git a/angular/src/app/app.module.ts b/angular/src/app/app.module.ts deleted file mode 100644 index 9fa2db4ef..000000000 --- a/angular/src/app/app.module.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { NgModule } from "@angular/core"; -import { FormsModule } from "@angular/forms"; -import { BrowserModule } from "@angular/platform-browser"; -import { ElizaProvider } from "src/connect/client.provider"; -import { ConnectModule } from "src/connect/connect.module"; - -import { AppComponent } from "./app.component"; - -@NgModule({ - imports: [ - AppComponent, - BrowserModule, - FormsModule, - ConnectModule.forRoot({ - baseUrl: "https://demo.connectrpc.com", - }), - ], - providers: [ElizaProvider], - bootstrap: [AppComponent], -}) -export class AppModule {} diff --git a/angular/src/connect/client.provider.ts b/angular/src/connect/client.provider.ts deleted file mode 100644 index 178f75be6..000000000 --- a/angular/src/connect/client.provider.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Provider } from "@angular/core"; -import { Transport } from "@connectrpc/connect"; -import { createObservableClient } from "./observable-client"; -import { TRANSPORT, ELIZA } from "./tokens"; -import { ElizaService } from "src/gen/connectrpc/eliza/v1/eliza_pb"; - -export const ElizaProvider: Provider = { - provide: ELIZA, - useFactory: (transport: Transport) => { - return createObservableClient(ElizaService, transport); - }, - deps: [TRANSPORT], -}; diff --git a/angular/src/connect/connect.module.ts b/angular/src/connect/connect.module.ts index bed519b32..d73210a06 100644 --- a/angular/src/connect/connect.module.ts +++ b/angular/src/connect/connect.module.ts @@ -1,29 +1,59 @@ -import { ModuleWithProviders, NgModule } from "@angular/core"; -import { Interceptor } from "@connectrpc/connect"; -import { createConnectTransport } from "@connectrpc/connect-web"; -import { INTERCEPTORS, TRANSPORT } from "./tokens"; +import { inject, InjectionToken, Provider } from "@angular/core"; +import { Interceptor, Transport } from "@connectrpc/connect"; +import { + createConnectTransport, + createGrpcWebTransport, +} from "@connectrpc/connect-web"; +import { DescService } from "@bufbuild/protobuf"; +import { createObservableClient, ObservableClient } from "./observable-client"; -@NgModule() -export class ConnectModule { - public static forRoot( - connectOptions: Omit< - Parameters[0], - "interceptors" - >, - ): ModuleWithProviders { - return { - ngModule: ConnectModule, - providers: [ - { - provide: TRANSPORT, - useFactory: (interceptors: Interceptor[]) => - createConnectTransport({ - ...connectOptions, - interceptors: interceptors, - }), - deps: [INTERCEPTORS], - }, - ], - }; - } +const TRANSPORT = new InjectionToken("connect.transport"); + +export const INTERCEPTORS = new InjectionToken( + "connect.interceptors", + { + factory: () => [], + }, +); + +export function createClientToken( + service: T, +): InjectionToken> { + return new InjectionToken(`client for ${service.typeName}`, { + factory() { + return createObservableClient(service, inject(TRANSPORT)); + }, + }); +} + +export function provideConnect( + options: Omit[0], "interceptors">, +): Provider[] { + return [ + { + provide: TRANSPORT, + useFactory: (interceptors: Interceptor[]) => + createConnectTransport({ + ...options, + interceptors, + }), + deps: [INTERCEPTORS], + }, + ]; +} + +export function provideGrpcWeb( + options: Omit[0], "interceptors">, +): Provider[] { + return [ + { + provide: TRANSPORT, + useFactory: (interceptors: Interceptor[]) => + createGrpcWebTransport({ + ...options, + interceptors, + }), + deps: [INTERCEPTORS], + }, + ]; } diff --git a/angular/src/connect/grpc-web.module.ts b/angular/src/connect/grpc-web.module.ts deleted file mode 100644 index bda912783..000000000 --- a/angular/src/connect/grpc-web.module.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { ModuleWithProviders, NgModule } from "@angular/core"; -import { Interceptor } from "@connectrpc/connect"; -import { createGrpcWebTransport } from "@connectrpc/connect-web"; -import { INTERCEPTORS, TRANSPORT } from "./tokens"; - -@NgModule() -export class GrpcWebModule { - public static forRoot( - grpcWebOptions: Omit< - Parameters[0], - "interceptors" - >, - ): ModuleWithProviders { - return { - ngModule: GrpcWebModule, - providers: [ - { - provide: TRANSPORT, - useFactory: (interceptors: Interceptor[]) => - createGrpcWebTransport({ - ...grpcWebOptions, - interceptors: interceptors, - }), - deps: [INTERCEPTORS], - }, - ], - }; - } -} diff --git a/angular/src/connect/tokens.ts b/angular/src/connect/tokens.ts index 57c5785da..b4db554dd 100644 --- a/angular/src/connect/tokens.ts +++ b/angular/src/connect/tokens.ts @@ -1,17 +1,7 @@ -import { InjectionToken } from "@angular/core"; -import type { Interceptor, Transport } from "@connectrpc/connect"; import { ElizaService } from "src/gen/connectrpc/eliza/v1/eliza_pb"; -import { ObservableClient } from "./observable-client"; +import { createClientToken } from "./connect.module"; -export const TRANSPORT = new InjectionToken("connect.transport"); +// Create an injection token for the Eliza service client +export const ELIZA = createClientToken(ElizaService); -export const INTERCEPTORS = new InjectionToken( - "connect.interceptors", - { - factory: () => [], - }, -); - -export const ELIZA = new InjectionToken>( - ElizaService.name, -); +// Additional client tokens representing Connect services could be added here diff --git a/angular/src/main.ts b/angular/src/main.ts index c7b673cf4..a878a05ed 100644 --- a/angular/src/main.ts +++ b/angular/src/main.ts @@ -1,12 +1,7 @@ -import { enableProdMode } from '@angular/core'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { appConfig } from "./app/app.config"; -import { AppModule } from './app/app.module'; -import { environment } from './environments/environment'; - -if (environment.production) { - enableProdMode(); -} - -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.error(err)); +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err), +);