Skip to content

Commit 48f9d95

Browse files
feat: add secondary entry point for redux connector
The redux-connector is the only extension that requires the @ngrx/store. By making it a secondary entry point, the @ngrx/store dependency is not applied to the other extensions. This fixes issue #79
1 parent 42d098b commit 48f9d95

File tree

24 files changed

+147
-524
lines changed

24 files changed

+147
-524
lines changed

README.md

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Starting with 18.0.0-rc.2, we have a [strict version dependency](#why-is-the-ver
2323

2424
| @ngrx/signals | @angular-architects/ngrx-toolkit |
2525
|----------------|----------------------------------|
26-
| 18.0.2 | 18.0.2 |
26+
| 18.0.2 | latest |
2727
| 18.0.0 | 18.0.0 |
2828
| 18.0.0-rc.3 | (not supported) |
2929
| 18.0.0-rc.2 | 18.0.0-rc.2.x |
@@ -35,7 +35,6 @@ To install it, run
3535
npm i @angular-architects/ngrx-toolkit
3636
```
3737

38-
3938
- [NgRx Toolkit](#ngrx-toolkit)
4039
- [Devtools: `withDevtools()`](#devtools-withdevtools)
4140
- [Redux: `withRedux()`](#redux-withredux)
@@ -54,7 +53,6 @@ npm i @angular-architects/ngrx-toolkit
5453
- [I have an idea for a new extension, can I contribute?](#i-have-an-idea-for-a-new-extension-can-i-contribute)
5554
- [I require a feature that is not available in a lower version. What should I do?](#i-require-a-feature-that-is-not-available-in-a-lower-version-what-should-i-do)
5655

57-
5856
## Devtools: `withDevtools()`
5957

6058
This extension is very easy to use. Just add it to a `signalStore`. Example:
@@ -68,13 +66,16 @@ export const FlightStore = signalStore(
6866
);
6967
```
7068

71-
The Signal Store does not use the Redux pattern, so there are no action names involved by default. Instead, every action is referred to as a Store Update.” However, if you want to customize the action name for better clarity, you can use the `updateState` method instead of `patchState`:
69+
The Signal Store does not use the Redux pattern, so there are no action names involved by default. Instead, every action is referred to as a "Store Update". However, if you want to customize the action name for better clarity, you can use the `updateState` method instead of `patchState`:
7270

7371
```typescript
74-
patchState(this.store, {loading: false});
72+
patchState(this.store, { loading: false });
7573

7674
// updateState is a wrapper around patchState and has an action name as second parameter
77-
updateState(this.store 'update loading', {loading: false});
75+
updateState(this.store
76+
'update loading', { loading: false }
77+
)
78+
;
7879
```
7980

8081
## Redux: `withRedux()`
@@ -347,7 +348,7 @@ public class UndoRedoComponent {
347348

348349
## Redux Connector for the NgRx Signal Store `createReduxState()`
349350

350-
The Redux Connector turns any `signalStore()` into a Gobal State Management Slice following the Redux pattern.
351+
The Redux Connector turns any `signalStore()` into a Global State Management Slice following the Redux pattern. It is available as secondary entry point, i.e. `import { createReduxState } from '@angular-architects/ngrx-toolkit/redux-connector'` and has a dependency to `@ngrx/store`.
351352

352353
It supports:
353354

@@ -357,7 +358,6 @@ It supports:
357358
✅ Auto-generated `provideNamedStore()` & `injectNamedStore()` Functions \
358359
✅ Global Action to Store Method Mappers \
359360

360-
361361
### Use a present Signal Store
362362

363363
```typescript
@@ -412,23 +412,23 @@ export const ticketActions = createActionGroup({
412412
```typescript
413413
export const { provideFlightStore, injectFlightStore } =
414414
createReduxState('flight', FlightStore, store => withActionMappers(
415-
mapAction(
416-
// Filtered Action
417-
ticketActions.flightsLoad,
418-
// Side-Effect
419-
store.loadFlights,
420-
// Result Action
421-
ticketActions.flightsLoaded),
422-
mapAction(
423-
// Filtered Actions
424-
ticketActions.flightsLoaded, ticketActions.flightsLoadedByPassenger,
425-
// State Updater Method (like Reducers)
426-
store.setFlights
427-
),
428-
mapAction(ticketActions.flightUpdate, store.updateFlight),
429-
mapAction(ticketActions.flightsClear, store.clearFlights),
430-
)
431-
);
415+
mapAction(
416+
// Filtered Action
417+
ticketActions.flightsLoad,
418+
// Side-Effect
419+
store.loadFlights,
420+
// Result Action
421+
ticketActions.flightsLoaded),
422+
mapAction(
423+
// Filtered Actions
424+
ticketActions.flightsLoaded, ticketActions.flightsLoadedByPassenger,
425+
// State Updater Method (like Reducers)
426+
store.setFlights
427+
),
428+
mapAction(ticketActions.flightUpdate, store.updateFlight),
429+
mapAction(ticketActions.flightsClear, store.clearFlights),
430+
)
431+
);
432432
```
433433

434434
### Register an Angular Dependency Injection Provider
@@ -476,6 +476,7 @@ export class FlightSearchReducConnectorComponent {
476476
}
477477
}
478478
```
479+
479480
## FAQ
480481

481482
### Why is the version range to the `@ngrx/signals` dependency so strict?
@@ -489,6 +490,7 @@ To ensure stability, we clone these internal types and run integration tests for
489490
Yes, please! We are always looking for new ideas and contributions.
490491

491492
Since we don't want to bloat the library, we are very selective about new features. You also have to provide the following:
493+
492494
- Good test coverage so that we can update it properly and don't have to call you 😉.
493495
- A use case showing the feature in action in the demo app of the repository.
494496
- An entry to the README.md.

apps/demo/src/app/category.store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { patchState, signalStore, withHooks } from '@ngrx/signals';
22
import { setAllEntities, withEntities } from '@ngrx/signals/entities';
3-
import { withDevtools } from 'ngrx-toolkit';
3+
import { withDevtools } from '@angular-architects/ngrx-toolkit';
44

55
export interface Category {
66
id: number;
Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,30 @@
11
import { FlightService } from '../shared/flight.service';
22

3-
import {
4-
signalStore, type,
5-
} from '@ngrx/signals';
3+
import { signalStore, type } from '@ngrx/signals';
64

75
import { withEntities } from '@ngrx/signals/entities';
8-
import { withCallState, withDataService, withUndoRedo } from 'ngrx-toolkit';
6+
import {
7+
withCallState,
8+
withDataService,
9+
withUndoRedo,
10+
} from '@angular-architects/ngrx-toolkit';
911
import { Flight } from '../shared/flight';
1012

1113
export const FlightBookingStore = signalStore(
1214
{ providedIn: 'root' },
1315
withCallState({
14-
collection: 'flight'
16+
collection: 'flight',
1517
}),
1618
withEntities({
1719
entity: type<Flight>(),
18-
collection: 'flight'
20+
collection: 'flight',
1921
}),
2022
withDataService({
2123
dataServiceType: FlightService,
2224
filter: { from: 'Paris', to: 'New York' },
23-
collection: 'flight'
25+
collection: 'flight',
2426
}),
2527
withUndoRedo({
2628
collections: ['flight'],
27-
}),
29+
})
2830
);
Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
import { FlightService } from '../shared/flight.service';
22

3-
import {
4-
signalStore,
5-
} from '@ngrx/signals';
3+
import { signalStore } from '@ngrx/signals';
64

75
import { withEntities } from '@ngrx/signals/entities';
8-
import { withCallState, withDataService, withUndoRedo } from 'ngrx-toolkit';
6+
import {
7+
withCallState,
8+
withDataService,
9+
withUndoRedo,
10+
} from '@angular-architects/ngrx-toolkit';
911
import { Flight } from '../shared/flight';
1012

1113
export const SimpleFlightBookingStore = signalStore(
1214
{ providedIn: 'root' },
1315
withCallState(),
1416
withEntities<Flight>(),
1517
withDataService({
16-
dataServiceType: FlightService,
18+
dataServiceType: FlightService,
1719
filter: { from: 'Paris', to: 'New York' },
1820
}),
19-
withUndoRedo(),
20-
);
21+
withUndoRedo()
22+
);
Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import { createReduxState, withActionMappers, mapAction } from "ngrx-toolkit";
2-
import { ticketActions } from "./actions";
3-
import { FlightStore } from "./store";
4-
1+
import { ticketActions } from './actions';
2+
import { FlightStore } from './store';
3+
import {
4+
createReduxState,
5+
withActionMappers,
6+
mapAction,
7+
} from '@angular-architects/ngrx-toolkit/redux-connector';
58

69
export const { provideFlightStore, injectFlightStore } =
710
/**
@@ -12,10 +15,19 @@ export const { provideFlightStore, injectFlightStore } =
1215
* - Selector Signals
1316
* - Dispatch
1417
*/
15-
createReduxState('flight', FlightStore, store => withActionMappers(
16-
mapAction(ticketActions.flightsLoad, store.loadFlights, ticketActions.flightsLoaded),
17-
mapAction(ticketActions.flightsLoaded, ticketActions.flightsLoadedByPassenger, store.setFlights),
18-
mapAction(ticketActions.flightUpdate, store.updateFlight),
19-
mapAction(ticketActions.flightsClear, store.clearFlights),
20-
)
21-
);
18+
createReduxState('flight', FlightStore, (store) =>
19+
withActionMappers(
20+
mapAction(
21+
ticketActions.flightsLoad,
22+
store.loadFlights,
23+
ticketActions.flightsLoaded
24+
),
25+
mapAction(
26+
ticketActions.flightsLoaded,
27+
ticketActions.flightsLoadedByPassenger,
28+
store.setFlights
29+
),
30+
mapAction(ticketActions.flightUpdate, store.updateFlight),
31+
mapAction(ticketActions.flightsClear, store.clearFlights)
32+
)
33+
);
Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,62 @@
11
import { computed, inject } from '@angular/core';
2-
import { patchState, signalStore, type, withComputed, withMethods } from '@ngrx/signals';
3-
import { removeAllEntities, setAllEntities, updateEntity, withEntities } from '@ngrx/signals/entities';
4-
import { reduxMethod } from 'ngrx-toolkit';
2+
import {
3+
patchState,
4+
signalStore,
5+
type,
6+
withComputed,
7+
withMethods,
8+
} from '@ngrx/signals';
9+
import {
10+
removeAllEntities,
11+
setAllEntities,
12+
updateEntity,
13+
withEntities,
14+
} from '@ngrx/signals/entities';
15+
import { reduxMethod } from '@angular-architects/ngrx-toolkit/redux-connector';
516
import { from, map, pipe, switchMap } from 'rxjs';
617
import { Flight } from '../../shared/flight';
718
import { FlightFilter, FlightService } from '../../shared/flight.service';
819

9-
1020
export const FlightStore = signalStore(
1121
{ providedIn: 'root' },
1222
// State
1323
withEntities({ entity: type<Flight>(), collection: 'flight' }),
1424
withEntities({ entity: type<number>(), collection: 'hide' }),
1525
// Selectors
1626
withComputed(({ flightEntities, hideEntities }) => ({
17-
filteredFlights: computed(() => flightEntities()
18-
.filter(flight => !hideEntities().includes(flight.id))),
27+
filteredFlights: computed(() =>
28+
flightEntities().filter((flight) => !hideEntities().includes(flight.id))
29+
),
1930
flightCount: computed(() => flightEntities().length),
2031
})),
2132
// Updater
22-
withMethods(store => ({
23-
setFlights: (state: { flights: Flight[] }) => patchState(store,
24-
setAllEntities(state.flights, { collection: 'flight' })),
25-
updateFlight: (state: { flight: Flight }) => patchState(store,
26-
updateEntity({ id: state.flight.id, changes: state.flight }, { collection: 'flight' })),
27-
clearFlights: () => patchState(store,
28-
removeAllEntities({ collection: 'flight' })),
33+
withMethods((store) => ({
34+
setFlights: (state: { flights: Flight[] }) =>
35+
patchState(
36+
store,
37+
setAllEntities(state.flights, { collection: 'flight' })
38+
),
39+
updateFlight: (state: { flight: Flight }) =>
40+
patchState(
41+
store,
42+
updateEntity(
43+
{ id: state.flight.id, changes: state.flight },
44+
{ collection: 'flight' }
45+
)
46+
),
47+
clearFlights: () =>
48+
patchState(store, removeAllEntities({ collection: 'flight' })),
2949
})),
3050
// Effects
3151
withMethods((store, flightService = inject(FlightService)) => ({
32-
loadFlights: reduxMethod<FlightFilter, { flights: Flight[] }>(pipe(
33-
switchMap(filter => from(
34-
flightService.load({ from: filter.from, to: filter.to })
35-
)),
36-
map(flights => ({ flights })),
37-
), store.setFlights),
38-
})),
52+
loadFlights: reduxMethod<FlightFilter, { flights: Flight[] }>(
53+
pipe(
54+
switchMap((filter) =>
55+
from(flightService.load({ from: filter.from, to: filter.to }))
56+
),
57+
map((flights) => ({ flights }))
58+
),
59+
store.setFlights
60+
),
61+
}))
3962
);

apps/demo/src/app/flight-search-with-pagination/flight-store.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import { FlightService } from '../shared/flight.service';
33
import { signalStore, type } from '@ngrx/signals';
44

55
import { withEntities } from '@ngrx/signals/entities';
6-
import { withCallState, withDataService, withPagination } from 'ngrx-toolkit';
6+
import {
7+
withCallState,
8+
withDataService,
9+
withPagination,
10+
} from '@angular-architects/ngrx-toolkit';
711
import { Flight } from '../shared/flight';
812

913
export const FlightBookingStore = signalStore(

apps/demo/src/app/flight-search/flight-store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
withDevtools,
66
withRedux,
77
updateState,
8-
} from 'ngrx-toolkit';
8+
} from '@angular-architects/ngrx-toolkit';
99
import { inject } from '@angular/core';
1010
import { HttpClient, HttpParams } from '@angular/common/http';
1111
import { map, switchMap } from 'rxjs';

apps/demo/src/app/shared/flight.service.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
22
import { Injectable } from '@angular/core';
33
import { Observable, firstValueFrom } from 'rxjs';
44
import { EntityId } from '@ngrx/signals/entities';
5-
import { DataService } from 'ngrx-toolkit';
5+
import { DataService } from '@angular-architects/ngrx-toolkit';
66
import { Flight } from './flight';
77

88
export type FlightFilter = {
99
from: string;
1010
to: string;
11-
}
11+
};
1212

1313
@Injectable({
14-
providedIn: 'root'
14+
providedIn: 'root',
1515
})
1616
export class FlightService implements DataService<Flight, FlightFilter> {
1717
baseUrl = `https://demo.angulararchitects.io/api`;
@@ -44,11 +44,7 @@ export class FlightService implements DataService<Flight, FlightFilter> {
4444
return firstValueFrom(this.find(filter.from, filter.to));
4545
}
4646

47-
private find(
48-
from: string,
49-
to: string,
50-
urgent = false
51-
): Observable<Flight[]> {
47+
private find(from: string, to: string, urgent = false): Observable<Flight[]> {
5248
let url = [this.baseUrl, 'flight'].join('/');
5349

5450
if (urgent) {
@@ -75,5 +71,4 @@ export class FlightService implements DataService<Flight, FlightFilter> {
7571
const url = [this.baseUrl, 'flight', flight.id].join('/');
7672
return this.http.delete<void>(url);
7773
}
78-
7974
}

apps/demo/src/app/todo-storage-sync/synced-todo-store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
updateEntity,
77
} from '@ngrx/signals/entities';
88
import { AddTodo, Todo } from '../todo-store';
9-
import { withStorageSync } from 'ngrx-toolkit';
9+
import { withStorageSync } from '@angular-architects/ngrx-toolkit';
1010

1111
export const SyncedTodoStore = signalStore(
1212
{ providedIn: 'root' },

0 commit comments

Comments
 (0)