Skip to content

Commit 80ccc52

Browse files
feat: update to ngrx 18 and refactorings
- upgrade to ngrx18 (toolkit compatibility to rc-1 and rc2) - switch from `signals` to `computed` for internal representation of computeds - upgrade nx to 19 - clone internal ngrx types to avoid breaking the encapsulation - remove SSR from demo app - increase bundle budget for demo app - remove test and eslint and only verify for build target in GitHub actions
1 parent 3e8e17b commit 80ccc52

23 files changed

+10675
-21536
lines changed

.github/workflows/build.yml

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
name: Test
2-
on: [push]
2+
on:
3+
push:
4+
branches:
5+
- main
6+
pull_request:
7+
branches:
8+
- main
39
jobs:
410
build:
511
runs-on: ubuntu-latest
@@ -9,6 +15,6 @@ jobs:
915
with:
1016
node-version: '18'
1117
cache: 'npm'
12-
- run: npm ci
13-
- run: npm run verify
18+
- run: npm i
19+
- run: npm run build:all
1420

apps/demo/project.json

+3-8
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,15 @@
2929
"@angular/material/prebuilt-themes/deeppurple-amber.css",
3030
"apps/demo/src/styles.css"
3131
],
32-
"scripts": [],
33-
"server": "apps/demo/src/main.server.ts",
34-
"prerender": true,
35-
"ssr": {
36-
"entry": "apps/demo/server.ts"
37-
}
32+
"scripts": []
3833
},
3934
"configurations": {
4035
"production": {
4136
"budgets": [
4237
{
4338
"type": "initial",
4439
"maximumWarning": "500kb",
45-
"maximumError": "1mb"
40+
"maximumError": "2mb"
4641
},
4742
{
4843
"type": "anyComponentStyle",
@@ -94,4 +89,4 @@
9489
}
9590
}
9691
}
97-
}
92+
}

apps/demo/server.ts

-57
This file was deleted.

apps/demo/src/app/app.config.server.ts

-9
This file was deleted.

apps/demo/src/app/app.config.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,9 @@ import { LayoutModule } from '@angular/cdk/layout';
88

99
export const appConfig: ApplicationConfig = {
1010
providers: [
11-
provideClientHydration(),
12-
provideRouter(appRoutes,
13-
withComponentInputBinding()),
11+
provideRouter(appRoutes, withComponentInputBinding()),
1412
provideAnimations(),
1513
provideHttpClient(),
1614
importProvidersFrom(LayoutModule),
17-
1815
],
1916
};

apps/demo/src/app/flight-search-data-service-dynamic/flight-booking.store.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ export const FlightBookingStore = signalStore(
1313
withCallState({
1414
collection: 'flight'
1515
}),
16-
withEntities({
17-
entity: type<Flight>(),
16+
withEntities({
17+
entity: type<Flight>(),
1818
collection: 'flight'
1919
}),
2020
withDataService({
21-
dataServiceType: FlightService,
21+
dataServiceType: FlightService,
2222
filter: { from: 'Paris', to: 'New York' },
2323
collection: 'flight'
2424
}),

apps/demo/src/app/test.ts

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { filter, interval, Observable, pipe, switchMap, tap } from 'rxjs';
2+
import {
3+
patchState,
4+
signalStore,
5+
signalStoreFeature,
6+
type,
7+
withHooks,
8+
withMethods,
9+
withState,
10+
} from '@ngrx/signals';
11+
import { rxMethod } from '@ngrx/signals/rxjs-interop';
12+
import { inject } from '@angular/core';
13+
import { HttpClient } from '@angular/common/http';
14+
15+
interface Person {
16+
firstname: string;
17+
lastname: string;
18+
}
19+
20+
function withEntityVersioner<Entity>() {
21+
return signalStoreFeature(
22+
{ methods: type<{ loader: () => Observable<Entity[]> }>() },
23+
withState({ version: 1, entities: new Array<Entity>() }),
24+
withMethods((store) => {
25+
return {
26+
update: rxMethod<unknown>(
27+
pipe(
28+
switchMap(() => store.loader()),
29+
filter((entities) => entities !== store.entities()),
30+
tap((entities) =>
31+
patchState(store, (value) => ({
32+
entities,
33+
version: value.version + 1,
34+
}))
35+
)
36+
)
37+
),
38+
};
39+
}),
40+
withHooks((store) => ({ onInit: () => store.update(interval(1000)) }))
41+
);
42+
}
43+
44+
signalStore(
45+
withMethods(() => {
46+
const httpClient = inject(HttpClient);
47+
return {
48+
loader() {
49+
return httpClient.get<Person[]>('someUrl');
50+
},
51+
};
52+
}),
53+
withEntityVersioner() // does not compile
54+
);

apps/demo/src/main.server.ts

-7
This file was deleted.

apps/demo/tsconfig.app.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"outDir": "../../dist/out-tsc",
55
"types": ["node"]
66
},
7-
"files": ["src/main.ts", "src/main.server.ts", "server.ts"],
7+
"files": ["src/main.ts"],
88
"include": ["src/**/*.d.ts"],
99
"exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"]
1010
}

libs/ngrx-toolkit/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"name": "@angular-architects/ngrx-toolkit",
3-
"version": "0.4.0",
3+
"version": "18.0.0-rc.2.0",
44
"license": "MIT",
55
"repository": {
66
"type": "GitHub",
77
"url": "https://github.com/angular-architects/ngrx-toolkit"
88
},
99
"peerDependencies": {
10-
"@ngrx/signals": "^17.2.0"
10+
"@ngrx/signals": "18.0.0-rc.1 || 18.0.0-rc.2"
1111
},
1212
"dependencies": {
1313
"tslib": "^2.3.0"
+1-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
// eslint-disable-next-line @typescript-eslint/ban-types
2-
export type Emtpy = {};
1+
export type Empty = {};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export type Prettify<Type extends {}> = {
2+
[Key in keyof Type]: Type[Key];
3+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* This file contains copies of types of the Signal Store which are not public.
3+
*
4+
* Since certain features depend on them, if we don't want to break
5+
* the encapsulation of @ngrx/signals, we decided to copy them.
6+
*
7+
* Since TypeScript is based on structural typing, we can get away with it.
8+
*
9+
* If @ngrx/signals changes its internal types, we catch them via integration
10+
* tests.
11+
*
12+
* Because of the "tight coupling", the toolkit doesn't have version range
13+
* to @ngrx/signal, but is very precise.
14+
*/
15+
import { Signal } from '@angular/core';
16+
import { EntityId } from '@ngrx/signals/entities';
17+
18+
export type SignalStoreFeatureResult = {
19+
state: object;
20+
computed: Record<string, Signal<unknown>>;
21+
methods: Record<string, Function>;
22+
};
23+
24+
export type EmptyFeatureResult = {
25+
state: {};
26+
computed: {};
27+
methods: {};
28+
};
29+
30+
// withEntites models
31+
export type EntityState<Entity> = {
32+
entityMap: Record<EntityId, Entity>;
33+
ids: EntityId[];
34+
};
35+
36+
export type EntityComputed<Entity> = {
37+
entities: Signal<Entity[]>;
38+
};
39+
40+
export type NamedEntityComputed<Entity, Collection extends string> = {
41+
[K in keyof EntityComputed<Entity> as `${Collection}${Capitalize<K>}`]: EntityComputed<Entity>[K];
42+
};

0 commit comments

Comments
 (0)