Skip to content

Commit

Permalink
fix(create-injection-token): add injector to create injection token (#78
Browse files Browse the repository at this point in the history
)

* feat: update nx

* fix(create-injection-token): allows injectFn by createInjectionToken to accept custom Injector
  • Loading branch information
nartc committed Sep 23, 2023
1 parent 3247aa8 commit b3ebe4b
Show file tree
Hide file tree
Showing 6 changed files with 916 additions and 682 deletions.
22 changes: 22 additions & 0 deletions docs/src/content/docs/utilities/create-injection-token.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,25 @@ export const [injectService, provideService] = createInjectionToken(serviceFacto
```

Note that if `token` is passed in and `isRoot: true`, `createInjectionToken` will throw an error.

### Injector

The `injectFn` returned by `createInjectionToken` also accepts a custom `Injector` to allow the consumers to call the `injectFn`
outside of an Injection Context.

```ts
function countFactory() {
return signal(1);
}

export const [injectCount] = createInjectionToken(countFactory);

@Component()
export class Counter {
#injector = inject(Injector);

ngOnInit() {
const counter = injectCount({ injector: this.#injector });
}
}
```
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { inject } from '@angular/core';
import { Injector, inject } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { createInjectionToken } from './create-injection-token';

Expand Down Expand Up @@ -85,4 +85,20 @@ describe(createInjectionToken.name, () => {
});
});
});

describe('given injection token', () => {
const [injectFn, provideFn] = createInjectionToken(() => 1);
it(`then throw no provider when invoked with an injector without providing`, () => {
const injector = Injector.create({ providers: [] });
expect(injectFn.bind(injectFn, { injector })).toThrowError(
/no provider/i
);
});

it(`then return correct value when invoked with an injector`, () => {
const injector = Injector.create({ providers: [provideFn()] });
const value = injectFn({ injector });
expect(value).toEqual(1);
});
});
});
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import {
Host,
InjectionToken,
Injector,
Optional,
Self,
SkipSelf,
inject,
runInInjectionContext,
type EnvironmentProviders,
type FactoryProvider,
type InjectOptions,
type Provider,
type Type,
} from '@angular/core';
import { assertInjector } from 'ngxtension/assert-injector';

type CreateInjectionTokenDep<TTokenType> =
| Type<TTokenType>
Expand Down Expand Up @@ -53,8 +56,14 @@ type InjectFn<
TFactoryReturn extends ReturnType<TFactory> = ReturnType<TFactory>
> = {
(): TFactoryReturn;
(injectOptions: InjectOptions & { optional?: false }): TFactoryReturn;
(injectOptions: InjectOptions): TFactoryReturn | null;
(
injectOptions: InjectOptions & { optional?: false } & {
injector?: Injector;
}
): TFactoryReturn;
(
injectOptions: InjectOptions & { injector?: Injector }
): TFactoryReturn | null;
};

export type CreateInjectionTokenReturn<
Expand All @@ -67,8 +76,15 @@ export type CreateInjectionTokenReturn<
];

function createInjectFn<TValue>(token: InjectionToken<TValue>) {
return (injectOptions?: InjectOptions) =>
inject(token, injectOptions as InjectOptions);
return function (
this: Function,
{ injector, ...injectOptions }: InjectOptions & { injector?: Injector } = {}
) {
injector = assertInjector(this, injector);
return runInInjectionContext(injector, () =>
inject(token, injectOptions as InjectOptions)
);
};
}

function createProvideFn<
Expand Down Expand Up @@ -122,6 +138,7 @@ export function createInjectionToken<
factory: TFactory,
options?: CreateInjectionTokenOptions<TFactory, TFactoryDeps>
): CreateInjectionTokenReturn<TFactory, TFactoryReturn> {
const tokenName = factory.name || factory.toString();
const opts =
options ??
({ isRoot: true } as CreateInjectionTokenOptions<TFactory, TFactoryDeps>);
Expand All @@ -135,17 +152,14 @@ createInjectionToken is creating a root InjectionToken but an external token is
`);
}

const token = new InjectionToken<TFactoryReturn>(
`Token for ${factory.name}`,
{
factory: () => {
if (opts.deps && Array.isArray(opts.deps)) {
return factory(...opts.deps.map((dep) => inject(dep)));
}
return factory();
},
}
);
const token = new InjectionToken<TFactoryReturn>(`Token for ${tokenName}`, {
factory: () => {
if (opts.deps && Array.isArray(opts.deps)) {
return factory(...opts.deps.map((dep) => inject(dep)));
}
return factory();
},
});

return [
createInjectFn(token) as CreateInjectionTokenReturn<
Expand All @@ -158,8 +172,7 @@ createInjectionToken is creating a root InjectionToken but an external token is
}

const token =
opts.token ||
new InjectionToken<TFactoryReturn>(`Token for ${factory.name}`);
opts.token || new InjectionToken<TFactoryReturn>(`Token for ${tokenName}`);
return [
createInjectFn(token) as CreateInjectionTokenReturn<
TFactory,
Expand Down
20 changes: 20 additions & 0 deletions migrations.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"migrations": [
{
"cli": "nx",
"version": "16.9.0-beta.1",
"description": "Replace imports of Module Federation utils frm @nx/devkit to @nx/webpack",
"implementation": "./src/migrations/update-16-9-0/migrate-mf-util-usage",
"package": "@nx/devkit",
"name": "update-16-9-0-migrate-mf-usage-to-webpack"
},
{
"cli": "nx",
"version": "16.8.2-beta.0",
"description": "Remove invalid options (strict, noInterop) for ES6 type modules.",
"factory": "./src/migrations/update-16-8-2/update-swcrc",
"package": "@nx/js",
"name": "16-8-2-update-swcrc"
}
]
}
26 changes: 13 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"@angular/platform-browser": "~16.2.5",
"@angular/platform-browser-dynamic": "~16.2.5",
"@angular/router": "~16.2.5",
"@swc/helpers": "~0.5.2",
"@swc/helpers": "0.5.2",
"rxjs": "~7.8.1",
"tslib": "^2.6.2",
"zone.js": "~0.13.3"
Expand All @@ -36,22 +36,22 @@
"@angular/cli": "~16.2.2",
"@angular/compiler-cli": "~16.2.5",
"@angular/language-service": "~16.2.5",
"@nx/angular": "16.8.1",
"@nx/cypress": "16.8.1",
"@nx/devkit": "16.8.1",
"@nx/eslint-plugin": "16.8.1",
"@nx/jest": "16.8.1",
"@nx/js": "16.8.1",
"@nx/linter": "16.8.1",
"@nx/plugin": "16.8.1",
"@nx/web": "16.8.1",
"@nx/workspace": "16.8.1",
"@nx/angular": "16.9.0",
"@nx/cypress": "16.9.0",
"@nx/devkit": "16.9.0",
"@nx/eslint-plugin": "16.9.0",
"@nx/jest": "16.9.0",
"@nx/js": "16.9.0",
"@nx/linter": "16.9.0",
"@nx/plugin": "16.9.0",
"@nx/web": "16.9.0",
"@nx/workspace": "16.9.0",
"@release-it/bumper": "^5.1.0",
"@release-it/conventional-changelog": "^7.0.1",
"@schematics/angular": "~16.2.2",
"@swc-node/register": "~1.6.7",
"@swc/cli": "~0.1.62",
"@swc/core": "~1.3.84",
"@swc/core": "1.3.87",
"@types/jest": "^29.5.4",
"@types/node": "20.6.0",
"@typescript-eslint/eslint-plugin": "^6.7.0",
Expand All @@ -67,7 +67,7 @@
"jest-preset-angular": "~13.1.1",
"jsonc-eslint-parser": "^2.3.0",
"ng-packagr": "~16.2.3",
"nx": "16.8.1",
"nx": "16.9.0",
"nx-cloud": "16.4.0",
"postcss": "^8.4.29",
"postcss-import": "~15.1.0",
Expand Down
Loading

0 comments on commit b3ebe4b

Please sign in to comment.