diff --git a/docs/src/content/docs/utilities/Injectors/create-injectable.md b/docs/src/content/docs/utilities/Injectors/create-injectable.md index 26f5ae7b..bd009313 100644 --- a/docs/src/content/docs/utilities/Injectors/create-injectable.md +++ b/docs/src/content/docs/utilities/Injectors/create-injectable.md @@ -13,19 +13,24 @@ The general difference is that rather than using a class, we use a `function` to create the injectable. Whatever the function returns is what will be the consumable public API of the service, everything else will be private. -Pass `{ providedIn: 'root' }` to the 2nd argument of `createInjectable` if you want to create a root service. +### `providedIn` -## Usage +By default, `createInjectable` returns a root service with `providedIn: 'root'`. You can override this by passing in a second argument to `createInjectable`: + +- `scoped`: The service will be scoped to where it is provided in (i.e: `providers` array) +- `platform`: The service will be scoped to the platform (i.e: `platform-browser`). This is recommended if you create services that are used across multiple apps on the same platform. ### Non-root Service ```ts // defining a service -export const MyService = createInjectable(() => { - const myState = signal(1); - - return { myState: myState.asReadonly() }; -}); +export const MyService = createInjectable( + () => { + const myState = signal(1); + return { myState: myState.asReadonly() }; + }, + { providedIn: 'scoped' }, +); ``` ```ts @@ -44,16 +49,26 @@ const myService = inject(MyService); ```ts // defining a root service -export const MyService = createInjectable( - () => { - const myState = signal(1); - return { myState: myState.asReadonly() }; - }, - { providedIn: 'root' }, -); +export const MyService = createInjectable(() => { + const myState = signal(1); + return { myState: myState.asReadonly() }; +}); ``` ```ts // using the service const myService = inject(MyService); ``` + +### Using a named function + +It is possible to use a named function as the `factory` instead of an arrow function. If a named function is used, the name of the function will be used as the name of the service constructor. + +```ts +export const MyService = createInjectable(function MyService() { + const myState = signal(1); + return { myState: myState.asReadonly() }; +}); + +console.log(MyService.name); // MyService +``` diff --git a/libs/ngxtension/create-injectable/src/create-injectable.spec.ts b/libs/ngxtension/create-injectable/src/create-injectable.spec.ts index 0e42cd53..d8e3f563 100644 --- a/libs/ngxtension/create-injectable/src/create-injectable.spec.ts +++ b/libs/ngxtension/create-injectable/src/create-injectable.spec.ts @@ -5,13 +5,10 @@ import { createInjectable } from './create-injectable'; describe(createInjectable.name, () => { it('should be able to access property returned from injectable', () => { let count = 0; - const MyInjectable = createInjectable( - () => { - count += 1; - return { someProp: 1 }; - }, - { providedIn: 'root' }, - ); + const MyInjectable = createInjectable(() => { + count += 1; + return { someProp: 1 }; + }); TestBed.runInInjectionContext(() => { // should be lazy until `inject()` is invoked @@ -34,10 +31,13 @@ describe(createInjectable.name, () => { it('should be able to provide non-root injectable', () => { let count = 0; - const MyInjectable = createInjectable(() => { - count += 1; - return { someProp: 1 }; - }); + const MyInjectable = createInjectable( + () => { + count += 1; + return { someProp: 1 }; + }, + { providedIn: 'scoped' }, + ); TestBed.configureTestingModule({ providers: [MyInjectable], diff --git a/libs/ngxtension/create-injectable/src/create-injectable.ts b/libs/ngxtension/create-injectable/src/create-injectable.ts index 6d0eeea3..896b0af8 100644 --- a/libs/ngxtension/create-injectable/src/create-injectable.ts +++ b/libs/ngxtension/create-injectable/src/create-injectable.ts @@ -2,14 +2,20 @@ import { Injectable, type Type } from '@angular/core'; export function createInjectable object>( factory: TFactory, - { providedIn }: { providedIn?: 'root' } = {}, + { providedIn = 'root' }: { providedIn?: 'root' | 'platform' | 'scoped' } = {}, ): Type> { - @Injectable({ providedIn: providedIn || null }) + @Injectable({ providedIn: providedIn === 'scoped' ? null : providedIn }) class _Injectable { constructor() { Object.assign(this, factory()); } } + if (factory.name) { + Object.defineProperty(_Injectable, 'name', { + value: `_Injectable_${factory.name}`, + }); + } + return _Injectable as Type>; }