Skip to main content

Code Standards

Coding Style

For the most part, we follow the official angular coding style guide

Git

Docs for our git workflow are in clickup

Unit Tests

Needs documentation

E2E Tests

Docs for writing E2E tests are in clickup

Feature Flags

Needs documentation

Injection Tokens

Needs documentation

Dates

Needs documentation

Http Calls and SignalR Hubs

Fetching Data

Use the httpResource API introduced in angular 19.2.

export class PlaygroundComponent {
search = signal('')
products = httpResource<{ products: any[] }>(() => {
const search = this.search()
if (!search) return 'https://dummyjson.com/products'
return `https://dummyjson.com/products/search?q=${search}`
})
}

If you need to fetch data using something other than a GET request, you can return a http configuration object instead of the url.

productsViaPost = httpResource<{ products: any[] }>(() => {
const search = this.search()
return {
url: 'https://dummyjson.com/products/search',
method: 'POST',
body: { q: search || null }
}
})

Mutating Data

Directly inject and use the HttpClient. Rarely do we reuse mutation endpoints, so there's no benefit to trying to consolidate them. (ie the only place we use the AddBackflowDevice endpoint is in the AddBackflowDeviceComponent)

export class PlaygroundComponent {
http = inject(HttpClient)

addProduct(title: string) {
this.http.post<{id: number, title: string}>('https://dummyjson.com/products/add', { title }).subscribe(res => {
alert(`Product ${res.title} added`)
})
}
}

Interceptors

In the http configuration object, you can pass in a HttpContext object that the interceptors utilize to know if they should run or not.

export const IS_CACHE_ENABLED = new HttpContextToken<boolean>(() => false);

...

productsViaPost = httpResource<{ products: any[] }>(() => {
const search = this.search()
return {
url: 'https://dummyjson.com/products/search',
method: 'POST',
body: { q: search },
//enable the caching interceptor
context: new HttpContext().set(IS_CACHE_ENABLED, true)
}
})

SignalR Hubs

Use the HubSubject class to manage signalr connections. It extends the rxjs subject to keep things familiar. It keeps track of the hub connections globally and reuses them.

export class GeneralTabComponent {
private ucUrl = inject(API_URLS).utilityConfigurationApiUrl
private hub$ = new HubSubject(`${this.ucUrl}/Users/Patch`)

...

public bufferedQueue$ = createBufferedQueue({
subject: this.patches$,
concurrency: signal(0),
statuses: this.statuses,
patchCallback: patches => this.hub$.invoke<PatchResponse>('SendPatchV2', this.routerData().userId, patches),
});

...

ngOnInit() {
this.hub$.subscribe()
}

ngOnDestroy() {
this.hub$.complete()
}
}

//Pass in the messages you wish to listen to on the backend
const notifications$ = new HubSubject(`${this.umUrl}/Hub`, 'ReportNotification', 'ProcessNotification')

//Send a message to the backend
const query = {
accounts: null,
groupBy: 'Account number'
}
notifications$.next('GetDepositReport', query, JSON.stringify(query))

//Will emit for any of the messages you passed in constructor
notifications$.subscribe(res => {
if (res.method == 'ReportNotification' || res.method == 'ProcessNotification') {
//show toast
}
})

//You can skip the HubSubject if you just need to do a single method invoke on the server
import { fromHub } from 'data'

...

public submitParamForm() {
//fromHub will connect to the hub and close it for you
fromHub(`${this.umUrl}`, 'GetDepositReport', this.form, JSON.stringify(this.form)).subscribe(() => {
//show toast
})
}