Prompt
When integrating with Angular’s DI and reactivity, follow these rules:
- Dependency access: For hook-like utilities, accept an optional Injector (or injector-derived path) so the function can be executed both inside and outside the normal injection context. Use Angular’s injection-context/assert-injector helpers to resolve dependencies from the Injector when provided.
- Effect lifecycle + teardown: If you use
effect(...)to keep signals in sync, add/propagate cleanup via the effectonCleanupargument wherever long-lived listeners/subscriptions are created. - Signal write semantics: If an effect updates a signal, ensure signal writes follow Angular’s expectations: use
allowSignalWrites: trueand wrap option reads inuntrackedwhen needed to avoid unintended tracking loops. - Modern HTTP interceptors: Prefer functional interceptors via
withInterceptors(...)over DI-provided interceptor classes. Also avoid redundantHttpClientModulewiring when usingprovideHttpClient(...).
Example pattern for effect-driven signal updates:
import { DestroyRef, effect, inject, signal, untracked, type Injector } from '@angular/core';
export function injectSomething(optionsFn = () => ({}), injector?: Injector) {
// resolve dependencies from injector / injection context here
return ((): any => {
const destroyRef = inject(DestroyRef);
const value = signal(0);
effect(
() => {
const opts = untracked(() => optionsFn());
// imperative signal update inside effect
value.set(opts.someValue);
},
{ allowSignalWrites: true }
);
return value;
})();
}
Applying this set of practices will make utilities safer to use across injection contexts, reduce lifecycle leaks, and align with Angular’s current HTTP interceptor direction.