Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion modules/store-devtools/spec/config.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Action } from '@ngrx/store';
import { Action, createAction } from '@ngrx/store';

import { createConfig, DEFAULT_NAME, noMonitor } from '../src/config';

Expand Down Expand Up @@ -41,6 +41,7 @@ describe('StoreDevtoolsOptions', () => {
function actionSanitizer(action: Action, id: number): Action {
return action;
}
const actionCreators = [createAction('[Counter] Increment')];
const config = createConfig({
maxAge: 20,
actionSanitizer,
Expand All @@ -53,6 +54,7 @@ describe('StoreDevtoolsOptions', () => {
features: {
test: true,
},
actionCreators,
});
expect(config).toEqual({
maxAge: 20,
Expand All @@ -69,6 +71,7 @@ describe('StoreDevtoolsOptions', () => {
test: true,
},
connectInZone: false,
actionCreators,
});
});

Expand Down
36 changes: 35 additions & 1 deletion modules/store-devtools/spec/extension.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
ReduxDevtoolsExtensionConfig,
ReduxDevtoolsExtensionConnection,
} from './../src/extension';
import { Action } from '@ngrx/store';
import { Action, createAction, props } from '@ngrx/store';

import { DevtoolsExtension, ReduxDevtoolsExtension } from '../src/extension';
import {
Expand Down Expand Up @@ -162,6 +162,40 @@ describe('DevtoolsExtension', () => {
expect(reduxDevtoolsExtension.connect).toHaveBeenCalledWith(options);
});

it('should connect with given action creators', () => {
const bookRented = createAction(
'[Books] Rent',
props<{ id: number; customerId: number }>()
);
const bookReturned = createAction(
'[Books] Return',
props<{ id: number }>()
);

const { devtoolsExtension, reduxDevtoolsExtension } = testSetup({
config: createConfig({
actionCreators: [bookRented, bookReturned],
}),
});

// Subscription needed or else extension connection will not be established.
devtoolsExtension.actions$.subscribe(() => null);
expect(reduxDevtoolsExtension.connect).toHaveBeenCalledWith(
expect.objectContaining({ actionCreators: [bookRented, bookReturned] })
);
});

it('should not add action creators to the connect options when not provided', () => {
const { devtoolsExtension, reduxDevtoolsExtension } = testSetup({
config: createConfig({}),
});

// Subscription needed or else extension connection will not be established.
devtoolsExtension.actions$.subscribe(() => null);
const [connectOptions] = reduxDevtoolsExtension.connect.mock.lastCall;
expect(connectOptions).not.toHaveProperty('actionCreators');
});

it('should connect with custom serializer', () => {
const customSerializer = {
replacer: (key: {}, value: any) => value,
Expand Down
9 changes: 8 additions & 1 deletion modules/store-devtools/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ActionReducer, Action } from '@ngrx/store';
import { ActionCreator, ActionReducer, Action } from '@ngrx/store';
import { InjectionToken } from '@angular/core';

export type ActionSanitizer = (action: Action, id: number) => Action;
Expand Down Expand Up @@ -120,6 +120,12 @@ export class StoreDevtoolsConfig {
* Angular zone or not. It is set to `false` by default.
*/
connectInZone?: boolean;

/**
* Action creators to make available in the extension's dispatcher,
* so actions can be dispatched manually from the extension.
*/
actionCreators?: ActionCreator[] | Record<string, ActionCreator>;
}

export const STORE_DEVTOOLS_CONFIG = new InjectionToken<StoreDevtoolsConfig>(
Expand Down Expand Up @@ -151,6 +157,7 @@ export function createConfig(
monitor: noMonitor,
actionSanitizer: undefined,
stateSanitizer: undefined,
actionCreators: undefined,
name: DEFAULT_NAME,
serialize: false,
logOnly: false,
Expand Down
6 changes: 5 additions & 1 deletion modules/store-devtools/src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Inject, Injectable, InjectionToken } from '@angular/core';
import { Action, UPDATE } from '@ngrx/store';
import { Action, ActionCreator, UPDATE } from '@ngrx/store';
import { EMPTY, Observable, of } from 'rxjs';
import {
catchError,
Expand Down Expand Up @@ -60,6 +60,7 @@ export interface ReduxDevtoolsExtensionConfig {
serialize?: boolean | SerializationOptions;
trace?: boolean | (() => string);
traceLimit?: number;
actionCreators?: ActionCreator[] | Record<string, ActionCreator>;
}

export interface ReduxDevtoolsExtension {
Expand Down Expand Up @@ -270,6 +271,9 @@ export class DevtoolsExtension {
if (config.maxAge !== false /* support === 0 */) {
extensionOptions.maxAge = config.maxAge;
}
if (config.actionCreators) {
extensionOptions.actionCreators = config.actionCreators;
}
return extensionOptions;
}

Expand Down
19 changes: 19 additions & 0 deletions projects/www/src/app/pages/guide/store-devtools/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,25 @@ array of strings as regex - actions types to be hidden / shown in the monitors,

function - called for every action before sending, takes state and action object, and returns `true` in case it allows sending the current data to the monitor, [more information here](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md#predicate).

### `actionCreators`

array or object of action creators to make available in the extension's dispatcher, so actions can be dispatched manually from the extension, [more information here](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md#actioncreators).

```typescript
const bookRented = createAction(
'[Books] Rent',
props<{ id: number }>()
);
const bookReturned = createAction(
'[Books] Return',
props<{ id: number }>()
);

provideStoreDevtools({
actionCreators: [bookRented, bookReturned],
});
```

### `connectInZone`

boolean - property determines whether the extension connection is established within the Angular zone or not. When `false`, the connection is established outside the Angular zone to prevent unnecessary change detection cycles. Default is `false`.
Expand Down
Loading