CreateInjectable ​

Defines a typed factory function for creating injectable dependencies that return a tuple of injection utilities. Offers an alternative to class-based Angular services with functional, composable dependency factories.

Usage ​

angular-ts
import { Component, computed, signal } from '@angular/core';
import { createInjectable } from '@signality/core';

export const [injectCounter, provideCounter] = createInjectable( 
  'Counter',
  (initialValue = 0) => {
    const count = signal(initialValue);
    const doubled = computed(() => count() * 2);

    function increment() {
      count.update(v => v + 1);
    }

    return { count: count.asReadonly(), doubled, increment };
  }
);

@Component({
  selector: 'app-counter',
  providers: [provideCounter(10)], 
  template: `
    <p>Count: {{ counter.count() }}</p>
    <p>Doubled: {{ counter.doubled() }}</p>
    <button (click)="counter.increment()">Increment</button>
  `,
})
export class CounterComponent {
  readonly counter = injectCounter(); 
}

createInjectable.root ​

The root variant creates an injectable that is provided in the application root by default.

angular-ts
import { computed, inject } from '@angular/core';
import { createInjectable } from '@signality/core';
import { AuthGateway } from './auth-gateway';

export const [injectAuth] = createInjectable.root('Auth', () => { 
  const authGateway = inject(AuthGateway);

  const me = rxResource({ stream: () => authGateway.getUserMe() });

  const isAdmin = computed(() => {
    return me().hasValue() && me.value().roles.includes('admin');
  });

  return { userMe: me.asReadonly(), isAdmin };
});

Parameters ​

ParameterTypeDescription
descriptionstringA descriptive label for the underlying InjectionToken (useful for debugging).
factoryFunctionA factory function that defines the injectable's logic.

Return Value ​

Returns a tuple containing:

IndexNameDescription
0injectFnA function to retrieve the instance (equivalent to inject).
1provideFnA function to provide the injectable in a providers array.
2injectionTokenThe underlying Angular InjectionToken.

Examples ​

Manual providing of root injectables ​

Even if an injectable is defined with .root, you can still provide it manually to override its value in a specific component tree (e.g., for testing or scoped configuration).

angular-ts
const [injectConfig, provideConfig] = createInjectable.root( 
  'Config',
  (apiUrl = 'https://api.example.com') => ({ apiUrl }) //
);

@Component({
  providers: [
    provideConfig('https://staging-api.example.com') 
  ],
})
export class StagingComponent {
  readonly config = injectConfig(); // Returns staging API URL
}

Using with custom Injector ​

The injectFn supports passing an explicit Injector if you need to inject the value outside of the typical injection context.

angular-ts
import { inject, Injector } from '@angular/core';

export class ManualInjection {
  private injector = inject(Injector); 

  someMethod() {
    const counter = injectCounter({ injector: this.injector }); 
  }
}

Type Definitions ​

ts
type CreateInjectableRef<Arguments extends any[], InjectReturn> = Readonly<
  [
    injectFn: InjectFn<InjectReturn>,
    provideFn: ProvideFn<Arguments>,
    injectionToken: InjectionToken<InjectReturn>
  ]
>;

interface CreateInjectableFn {
  <Arguments extends any[], Return>(
    description: string,
    factory: Factory<Arguments, Return>,
  ): CreateInjectableRef<Arguments, Return>;

  root: <Arguments extends any[], Return>(
    description: string,
    factory: (...args: OptionalArgs<Arguments>) => Return,
  ) => CreateInjectableRef<OptionalArgs<Arguments>, Return>;
}
Edit this page on GitHub Last updated: Apr 27, 2026, 21:07:43