Skip to content

Commit 843c38f

Browse files
committed
feat(with-storage-sync)!: support indexedDB
BREAKING CHANGE: The storage argument has been renamed to storageType, and its value must now be one of the following strings: 'localStorage', 'sessionStorage', or 'IndexedDB'. Additionally, when 'IndexedDB' is specified, two new arguments, `storeName` and `dbName`, have been added.
1 parent 8203d33 commit 843c38f

22 files changed

+810
-225
lines changed

apps/demo/src/app/app.component.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
>Redux Connector</a
2020
>
2121
<a mat-list-item routerLink="/todo-storage-sync">withStorageSync</a>
22+
<a mat-list-item routerLink="/todo-indexeddb-sync"
23+
>withStorageSync(IndexedDB)</a
24+
>
2225
<a mat-list-item routerLink="/reset">withReset</a>
2326
<a mat-list-item routerLink="/immutable-state">withImmutableState</a>
2427
</mat-nav-list>

apps/demo/src/app/devtools/todo-detail.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { Component, effect, inject, input } from '@angular/core';
22
import { MatCardModule } from '@angular/material/card';
3-
import { Todo } from './todo-store';
43
import { patchState, signalStore, withHooks, withState } from '@ngrx/signals';
54
import {
65
renameDevtoolsName,
76
withDevtools,
87
withGlitchTracking,
98
withMapper,
109
} from '@angular-architects/ngrx-toolkit';
10+
import { Todo } from '../shared/todo.service';
1111

1212
/**
1313
* This Store can be instantiated multiple times, if the user

apps/demo/src/app/devtools/todo-store.ts

Lines changed: 5 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,8 @@ import {
1212
withEntities,
1313
} from '@ngrx/signals/entities';
1414
import { updateState, withDevtools } from '@angular-architects/ngrx-toolkit';
15-
import { computed } from '@angular/core';
16-
17-
export interface Todo {
18-
id: number;
19-
name: string;
20-
finished: boolean;
21-
description?: string;
22-
deadline?: Date;
23-
}
24-
25-
export type AddTodo = Omit<Todo, 'id'>;
15+
import { computed, inject } from '@angular/core';
16+
import { Todo, AddTodo, TodoService } from '../shared/todo.service';
2617

2718
export const TodoStore = signalStore(
2819
{ providedIn: 'root' },
@@ -72,41 +63,9 @@ export const TodoStore = signalStore(
7263
),
7364
})),
7465
withHooks({
75-
onInit: (store) => {
76-
store.add({
77-
name: 'Go for a Walk',
78-
finished: false,
79-
description:
80-
'Go for a walk in the park to relax and enjoy nature. Walking is a great way to clear your mind and get some exercise. It can help reduce stress and improve your mood. Make sure to wear comfortable shoes and bring a bottle of water. Enjoy the fresh air and take in the scenery around you.',
81-
});
82-
83-
store.add({
84-
name: 'Read a Book',
85-
finished: false,
86-
description:
87-
'Spend some time reading a book. It can be a novel, a non-fiction book, or any other genre you enjoy. Reading can help you relax and learn new things.',
88-
});
89-
90-
store.add({
91-
name: 'Write a Journal',
92-
finished: false,
93-
description:
94-
'Take some time to write in your journal. Reflect on your day, your thoughts, and your feelings. Journaling can be a great way to process emotions and document your life.',
95-
});
96-
97-
store.add({
98-
name: 'Exercise',
99-
finished: false,
100-
description:
101-
'Do some physical exercise. It can be a workout, a run, or any other form of exercise you enjoy. Exercise is important for maintaining physical and mental health.',
102-
});
103-
104-
store.add({
105-
name: 'Cook a Meal',
106-
finished: false,
107-
description:
108-
'Prepare a meal for yourself or your family. Cooking can be a fun and rewarding activity. Try out a new recipe or make one of your favorite dishes.',
109-
});
66+
onInit: (store, todoService = inject(TodoService)) => {
67+
const todos = todoService.getData();
68+
todos.forEach((todo) => store.add(todo));
11069
},
11170
})
11271
);

apps/demo/src/app/devtools/todo.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { MatCheckboxModule } from '@angular/material/checkbox';
33
import { MatIconModule } from '@angular/material/icon';
44
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
55
import { SelectionModel } from '@angular/cdk/collections';
6-
import { Todo, TodoStore } from './todo-store';
6+
import { TodoStore } from './todo-store';
77
import { TodoDetailComponent } from './todo-detail.component';
88
import { FormsModule } from '@angular/forms';
9+
import { Todo } from '../shared/todo.service';
910

1011
@Component({
1112
selector: 'demo-todo',

apps/demo/src/app/lazy-routes.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { FlightSearchWithPaginationComponent } from './flight-search-with-pagina
99
import { FlightSearchReducConnectorComponent } from './flight-search-redux-connector/flight-search.component';
1010
import { provideFlightStore } from './flight-search-redux-connector/+state/redux';
1111
import { TodoComponent } from './devtools/todo.component';
12+
import { TodoIndexeddbSyncComponent } from './todo-indexeddb-sync/todo-indexeddb-sync.component';
1213

1314
export const lazyRoutes: Route[] = [
1415
{ path: 'todo', component: TodoComponent },
@@ -28,6 +29,7 @@ export const lazyRoutes: Route[] = [
2829
},
2930
{ path: 'flight-edit-dynamic/:id', component: FlightEditDynamicComponent },
3031
{ path: 'todo-storage-sync', component: TodoStorageSyncComponent },
32+
{ path: 'todo-indexeddb-sync', component: TodoIndexeddbSyncComponent },
3133
{
3234
path: 'flight-search-redux-connector',
3335
providers: [provideFlightStore()],
@@ -44,5 +46,5 @@ export const lazyRoutes: Route[] = [
4446
import('./immutable-state/immutable-state.component').then(
4547
(m) => m.ImmutableStateComponent
4648
),
47-
},
49+
}
4850
];
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { Injectable } from '@angular/core';
2+
3+
export interface Todo {
4+
id: number;
5+
name: string;
6+
finished: boolean;
7+
description?: string;
8+
deadline?: Date;
9+
}
10+
11+
export type AddTodo = Omit<Todo, 'id'>;
12+
13+
@Injectable({ providedIn: 'root' })
14+
export class TodoService {
15+
getData(): AddTodo[] {
16+
return [
17+
{
18+
name: 'Go for a Walk',
19+
finished: false,
20+
description:
21+
'Go for a walk in the park to relax and enjoy nature. Walking is a great way to clear your mind and get some exercise. It can help reduce stress and improve your mood. Make sure to wear comfortable shoes and bring a bottle of water. Enjoy the fresh air and take in the scenery around you.',
22+
},
23+
{
24+
name: 'Read a Book',
25+
finished: false,
26+
description:
27+
'Spend some time reading a book. It can be a novel, a non-fiction book, or any other genre you enjoy. Reading can help you relax and learn new things.',
28+
},
29+
{
30+
name: 'Write a Journal',
31+
finished: false,
32+
description:
33+
'Take some time to write in your journal. Reflect on your day, your thoughts, and your feelings. Journaling can be a great way to process emotions and document your life.',
34+
},
35+
{
36+
name: 'Exercise',
37+
finished: false,
38+
description:
39+
'Do some physical exercise. It can be a workout, a run, or any other form of exercise you enjoy. Exercise is important for maintaining physical and mental health.',
40+
},
41+
{
42+
name: 'Cook a Meal',
43+
finished: false,
44+
description:
45+
'Prepare a meal for yourself or your family. Cooking can be a fun and rewarding activity. Try out a new recipe or make one of your favorite dishes.',
46+
},
47+
];
48+
}
49+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { patchState, signalStore, withHooks, withMethods } from '@ngrx/signals';
2+
import {
3+
withEntities,
4+
setEntity,
5+
removeEntity,
6+
updateEntity,
7+
} from '@ngrx/signals/entities';
8+
import { withStorageSync } from '@angular-architects/ngrx-toolkit';
9+
import { inject } from '@angular/core';
10+
import { Todo, TodoService, AddTodo } from '../shared/todo.service';
11+
12+
export const SyncedTodoStore = signalStore(
13+
{ providedIn: 'root' },
14+
withEntities<Todo>(),
15+
withStorageSync({
16+
storageType: 'indexedDB',
17+
dbName: 'todos',
18+
storeName: 'todos',
19+
}),
20+
withMethods((store) => {
21+
let currentId = 0;
22+
return {
23+
add(todo: AddTodo) {
24+
patchState(store, setEntity({ id: ++currentId, ...todo }));
25+
},
26+
27+
remove(id: number) {
28+
patchState(store, removeEntity(id));
29+
},
30+
31+
toggleFinished(id: number): void {
32+
const todo = store.entityMap()[id];
33+
patchState(
34+
store,
35+
updateEntity({ id, changes: { finished: !todo.finished } })
36+
);
37+
},
38+
};
39+
}),
40+
withHooks({
41+
onInit(store, todoService = inject(TodoService)) {
42+
const todos = todoService.getData();
43+
todos.forEach((todo) => store.add(todo));
44+
},
45+
})
46+
);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<h2>StorageType:IndexedDB</h2>
2+
<mat-table [dataSource]="dataSource" class="mat-elevation-z8">
3+
<h2>IndexedDB</h2>
4+
<!-- Checkbox Column -->
5+
<ng-container matColumnDef="finished">
6+
<mat-header-cell *matHeaderCellDef></mat-header-cell>
7+
<mat-cell *matCellDef="let row" class="actions">
8+
<mat-checkbox
9+
(click)="$event.stopPropagation()"
10+
(change)="checkboxLabel(row)"
11+
[checked]="row.finished"
12+
>
13+
</mat-checkbox>
14+
<mat-icon (click)="removeTodo(row)">delete</mat-icon>
15+
</mat-cell>
16+
</ng-container>
17+
18+
<!-- Name Column -->
19+
<ng-container matColumnDef="name">
20+
<mat-header-cell *matHeaderCellDef>Name</mat-header-cell>
21+
<mat-cell *matCellDef="let element">{{ element.name }}</mat-cell>
22+
</ng-container>
23+
24+
<!-- Description Column -->
25+
<ng-container matColumnDef="description">
26+
<mat-header-cell *matHeaderCellDef>Description</mat-header-cell>
27+
<mat-cell *matCellDef="let element">{{ element.description }}</mat-cell>
28+
</ng-container>
29+
30+
<!-- Deadline Column -->
31+
<ng-container matColumnDef="deadline">
32+
<mat-header-cell mat-header-cell *matHeaderCellDef
33+
>Deadline</mat-header-cell
34+
>
35+
<mat-cell mat-cell *matCellDef="let element">{{
36+
element.deadline
37+
}}</mat-cell>
38+
</ng-container>
39+
40+
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
41+
<mat-row
42+
*matRowDef="let row; columns: displayedColumns"
43+
(click)="selection.toggle(row)"
44+
></mat-row>
45+
</mat-table>

apps/demo/src/app/todo-indexeddb-sync/todo-indexeddb-sync.component.scss

Whitespace-only changes.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Component, effect, inject } from '@angular/core';
2+
import { MatCheckboxModule } from '@angular/material/checkbox';
3+
import { MatIconModule } from '@angular/material/icon';
4+
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
5+
import { SyncedTodoStore } from './synced-todo-store';
6+
import { SelectionModel } from '@angular/cdk/collections';
7+
import { Todo } from '../shared/todo.service';
8+
9+
@Component({
10+
selector: 'demo-todo-indexeddb-sync',
11+
imports: [MatCheckboxModule, MatIconModule, MatTableModule],
12+
templateUrl: './todo-indexeddb-sync.component.html',
13+
styleUrl: './todo-indexeddb-sync.component.scss',
14+
})
15+
export class TodoIndexeddbSyncComponent {
16+
todoStore = inject(SyncedTodoStore);
17+
18+
displayedColumns: string[] = ['finished', 'name', 'description', 'deadline'];
19+
dataSource = new MatTableDataSource<Todo>([]);
20+
selection = new SelectionModel<Todo>(true, []);
21+
22+
constructor() {
23+
effect(() => {
24+
this.dataSource.data = this.todoStore.entities();
25+
});
26+
}
27+
28+
checkboxLabel(todo: Todo) {
29+
this.todoStore.toggleFinished(todo.id);
30+
}
31+
32+
removeTodo(todo: Todo) {
33+
this.todoStore.remove(todo.id);
34+
}
35+
}

0 commit comments

Comments
 (0)