From 1510fe95050ad70144daeecfaa1fe837ca727935 Mon Sep 17 00:00:00 2001 From: Timo Stamm Date: Tue, 17 Dec 2024 11:31:58 +0100 Subject: [PATCH 1/6] Remove ng modules Signed-off-by: Timo Stamm Signed-off-by: Steve Ayers --- angular/src/app/app.component.ts | 18 ++---- angular/src/app/app.module.ts | 21 ------ angular/src/connect/client.provider.ts | 13 ---- angular/src/connect/connect.module.ts | 88 ++++++++++++++++++-------- angular/src/connect/grpc-web.module.ts | 29 --------- angular/src/connect/tokens.ts | 17 +---- angular/src/main.ts | 28 ++++---- 7 files changed, 85 insertions(+), 129 deletions(-) delete mode 100644 angular/src/app/app.module.ts delete mode 100644 angular/src/connect/client.provider.ts delete mode 100644 angular/src/connect/grpc-web.module.ts 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.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..87e14f8d8 100644 --- a/angular/src/connect/connect.module.ts +++ b/angular/src/connect/connect.module.ts @@ -1,29 +1,63 @@ -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], - }, - ], - }; - } +export 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< + Parameters[0], + "interceptors" + >, +): Provider[] { + return [ + { + provide: TRANSPORT, + useFactory: (interceptors: Interceptor[]) => + createConnectTransport({ + ...options, + interceptors, + }), + deps: [INTERCEPTORS], + }, + ]; +} + +export function provideGrpcWeb( + options: Omit< + Parameters[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..67394bc61 100644 --- a/angular/src/connect/tokens.ts +++ b/angular/src/connect/tokens.ts @@ -1,17 +1,4 @@ -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"); - -export const INTERCEPTORS = new InjectionToken( - "connect.interceptors", - { - factory: () => [], - }, -); - -export const ELIZA = new InjectionToken>( - ElizaService.name, -); +export const ELIZA = createClientToken(ElizaService); diff --git a/angular/src/main.ts b/angular/src/main.ts index c7b673cf4..134f7d327 100644 --- a/angular/src/main.ts +++ b/angular/src/main.ts @@ -1,12 +1,18 @@ -import { enableProdMode } from '@angular/core'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +/* + * Protractor support is deprecated in Angular. + * Protractor is used in this example for compatibility with Angular documentation tools. + */ +import { bootstrapApplication, provideProtractorTestingSupport } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { provideConnect } from "./connect/connect.module"; -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, { + providers: [ + provideConnect({ + baseUrl: "https://demo.connectrpc.com", + }), + provideProtractorTestingSupport(), + ], +}).catch((err) => + console.error(err), +); From 76b22c3319d6d22c0134df8c84d4407924ed0ac5 Mon Sep 17 00:00:00 2001 From: Timo Stamm Date: Tue, 17 Dec 2024 11:33:23 +0100 Subject: [PATCH 2/6] format imports Signed-off-by: Timo Stamm Signed-off-by: Steve Ayers --- angular/src/connect/connect.module.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/angular/src/connect/connect.module.ts b/angular/src/connect/connect.module.ts index 87e14f8d8..b439f36ed 100644 --- a/angular/src/connect/connect.module.ts +++ b/angular/src/connect/connect.module.ts @@ -1,8 +1,8 @@ -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"; +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"; export const TRANSPORT = new InjectionToken("connect.transport"); From 78df450c4aa0d7af91d021dea9ab99f6411c9d7d Mon Sep 17 00:00:00 2001 From: Steve Ayers Date: Tue, 17 Dec 2024 10:30:18 -0500 Subject: [PATCH 3/6] Fix test Signed-off-by: Steve Ayers --- angular/src/app/app.component.spec.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) 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(); }); From ff69f17350daf1ddd695016652b56b559f8db58d Mon Sep 17 00:00:00 2001 From: Timo Stamm Date: Tue, 17 Dec 2024 16:50:44 +0100 Subject: [PATCH 4/6] Use app.config.ts Signed-off-by: Timo Stamm Signed-off-by: Steve Ayers --- angular/src/app/app.config.ts | 12 ++++++++++++ angular/src/main.ts | 17 +++-------------- 2 files changed, 15 insertions(+), 14 deletions(-) create mode 100644 angular/src/app/app.config.ts 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/main.ts b/angular/src/main.ts index 134f7d327..a878a05ed 100644 --- a/angular/src/main.ts +++ b/angular/src/main.ts @@ -1,18 +1,7 @@ -/* - * Protractor support is deprecated in Angular. - * Protractor is used in this example for compatibility with Angular documentation tools. - */ -import { bootstrapApplication, provideProtractorTestingSupport } from '@angular/platform-browser'; +import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; -import { provideConnect } from "./connect/connect.module"; +import { appConfig } from "./app/app.config"; -bootstrapApplication(AppComponent, { - providers: [ - provideConnect({ - baseUrl: "https://demo.connectrpc.com", - }), - provideProtractorTestingSupport(), - ], -}).catch((err) => +bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err), ); From 68b684a98bf26c1150820482883accfa37f9b5ab Mon Sep 17 00:00:00 2001 From: Steve Ayers Date: Wed, 18 Dec 2024 10:19:49 -0500 Subject: [PATCH 5/6] Cleanup Signed-off-by: Steve Ayers --- angular/src/connect/connect.module.ts | 43 +++++++++++---------------- angular/src/connect/tokens.ts | 3 ++ 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/angular/src/connect/connect.module.ts b/angular/src/connect/connect.module.ts index b439f36ed..a07ee202e 100644 --- a/angular/src/connect/connect.module.ts +++ b/angular/src/connect/connect.module.ts @@ -1,34 +1,30 @@ import { inject, InjectionToken, Provider } from "@angular/core"; import { Interceptor, Transport } from "@connectrpc/connect"; -import { createConnectTransport, createGrpcWebTransport } from "@connectrpc/connect-web"; +import { + createConnectTransport, + createGrpcWebTransport, +} from "@connectrpc/connect-web"; import { DescService } from "@bufbuild/protobuf"; import { createObservableClient, ObservableClient } from "./observable-client"; -export const TRANSPORT = new InjectionToken("connect.transport"); +const TRANSPORT = new InjectionToken("connect.transport"); -export const INTERCEPTORS = new InjectionToken( - "connect.interceptors", - { - factory: () => [], - }, -); +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 createClientToken( + service: T, +): InjectionToken> { + return new InjectionToken(`client for ${service.typeName}`, { + factory() { + return createObservableClient(service, inject(TRANSPORT)); + }, + }); } export function provideConnect( - options: Omit< - Parameters[0], - "interceptors" - >, + options: Omit[0], "interceptors">, ): Provider[] { return [ { @@ -44,10 +40,7 @@ export function provideConnect( } export function provideGrpcWeb( - options: Omit< - Parameters[0], - "interceptors" - >, + options: Omit[0], "interceptors">, ): Provider[] { return [ { diff --git a/angular/src/connect/tokens.ts b/angular/src/connect/tokens.ts index 67394bc61..b4db554dd 100644 --- a/angular/src/connect/tokens.ts +++ b/angular/src/connect/tokens.ts @@ -1,4 +1,7 @@ import { ElizaService } from "src/gen/connectrpc/eliza/v1/eliza_pb"; import { createClientToken } from "./connect.module"; +// Create an injection token for the Eliza service client export const ELIZA = createClientToken(ElizaService); + +// Additional client tokens representing Connect services could be added here From 1fe505ef7883c814bc4ee495516b306ee7431b51 Mon Sep 17 00:00:00 2001 From: Steve Ayers Date: Wed, 18 Dec 2024 10:27:16 -0500 Subject: [PATCH 6/6] Export Signed-off-by: Steve Ayers --- angular/src/connect/connect.module.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/angular/src/connect/connect.module.ts b/angular/src/connect/connect.module.ts index a07ee202e..d73210a06 100644 --- a/angular/src/connect/connect.module.ts +++ b/angular/src/connect/connect.module.ts @@ -9,9 +9,12 @@ import { createObservableClient, ObservableClient } from "./observable-client"; const TRANSPORT = new InjectionToken("connect.transport"); -const INTERCEPTORS = new InjectionToken("connect.interceptors", { - factory: () => [], -}); +export const INTERCEPTORS = new InjectionToken( + "connect.interceptors", + { + factory: () => [], + }, +); export function createClientToken( service: T,