Skip to content

Commit a454214

Browse files
authored
feat(core): add singleton injection token factory (#3261)
1 parent c3ea0bf commit a454214

File tree

4 files changed

+130
-0
lines changed

4 files changed

+130
-0
lines changed

libs/core/src/injection-tokens/public_api.ts

+2
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ export * from './config.type';
66
export * from './config.factory';
77
export * from './services.type';
88
export * from './services.factory';
9+
export * from './singleton.type';
10+
export * from './singleton.factory';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import {
2+
Injectable,
3+
Type,
4+
} from '@angular/core';
5+
import { TestBed } from '@angular/core/testing';
6+
import { faker } from '@faker-js/faker/locale/en_US';
7+
8+
import { DaffSingletonInjectionToken } from '@daffodil/core';
9+
10+
import { createSingletonInjectionToken } from './singleton.factory';
11+
12+
interface TestType {
13+
get(): string;
14+
}
15+
16+
@Injectable({
17+
providedIn: 'root',
18+
})
19+
class Test1 implements TestType {
20+
get() {
21+
return 'test1';
22+
}
23+
}
24+
25+
describe('@daffodil/core | createSingletonInjectionToken', () => {
26+
let name: string;
27+
let value: Type<TestType>;
28+
29+
let result: DaffSingletonInjectionToken<TestType>;
30+
31+
beforeEach(() => {
32+
name = faker.random.word();
33+
value = Test1;
34+
result = createSingletonInjectionToken(name);
35+
});
36+
37+
it('should return a token', () => {
38+
expect(result.token.toString()).toContain(name);
39+
});
40+
41+
it('should return a provider', () => {
42+
const res = result.provider(value);
43+
expect(res.provide).toEqual(result.token);
44+
expect(res.useExisting).toEqual(value);
45+
});
46+
});
47+
48+
describe('@daffodil/core | createSingletonInjectionToken | Integration', () => {
49+
let name: string;
50+
let value: Type<TestType>;
51+
52+
let result: DaffSingletonInjectionToken<TestType>;
53+
54+
beforeEach(() => {
55+
name = faker.random.word();
56+
value = Test1;
57+
result = createSingletonInjectionToken(name);
58+
});
59+
60+
describe('when value are provided', () => {
61+
beforeEach(() => {
62+
TestBed.configureTestingModule({
63+
providers: [
64+
result.provider(value),
65+
],
66+
});
67+
});
68+
69+
it('should inject the value', () => {
70+
expect(TestBed.inject(result.token).get()).toEqual('test1');
71+
});
72+
});
73+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {
2+
InjectionToken,
3+
Type,
4+
} from '@angular/core';
5+
6+
import { DaffSingletonInjectionToken } from './singleton.type';
7+
import {
8+
TokenDesc,
9+
TokenOptions,
10+
} from './token-constuctor-params.type';
11+
12+
/**
13+
* Creates an injection token/provider pair for a DI token that holds a singleton service.
14+
*
15+
* See {@link DaffSingletonInjectionToken}.
16+
*/
17+
export const createSingletonInjectionToken = <T = unknown>(
18+
desc: TokenDesc<T>,
19+
options?: TokenOptions<T>,
20+
): DaffSingletonInjectionToken<T> => {
21+
const token = new InjectionToken<T>(
22+
desc,
23+
options,
24+
);
25+
const provider = <R extends T = T>(klass: Type<R>) => ({
26+
provide: token,
27+
useExisting: klass,
28+
});
29+
30+
return {
31+
token,
32+
provider,
33+
};
34+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import {
2+
ExistingProvider,
3+
InjectionToken,
4+
Type,
5+
} from '@angular/core';
6+
7+
/**
8+
* A injection token to hold and provide a singleton service.
9+
*/
10+
export interface DaffSingletonInjectionToken<T = unknown> {
11+
/**
12+
* The injection token.
13+
* Its default value is an empty array.
14+
*/
15+
token: InjectionToken<T>;
16+
17+
/**
18+
* A helper function to provide the service class to the token.
19+
*/
20+
provider: <R extends T = T>(klass: Type<R>) => ExistingProvider;
21+
}

0 commit comments

Comments
 (0)