Skip to content

feat(with-storage-sync): indexeddb support #134

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
eb4bc3f
feat(with-storage-sync)!: support indexedDB
mzkmnk Jan 18, 2025
2d87998
chore(with-storage-sync): move with-storage-sync files
mzkmnk Feb 13, 2025
eb294b0
chore(with-storage-sync): storage factory (wip)
mzkmnk Feb 13, 2025
70f7b64
feat(with-storage-sync): wip add local and session storage services
mzkmnk Feb 14, 2025
3d8db6d
feat(with-storage-sync): enhance storage sync with local and indexedD…
mzkmnk Feb 14, 2025
8189b66
feat(with-storage-sync): add reset functionality and improve storage …
mzkmnk Feb 15, 2025
3002f0f
refactor(indexeddb.service): rename parameter for clarity in IndexedD…
mzkmnk Feb 15, 2025
230c740
refactor(with-storage-sync): simplify withStorageSync function parame…
mzkmnk Feb 15, 2025
110ccc3
chore(with-storage-sync): move with-storage-sync(and test) to with-st…
mzkmnk Feb 18, 2025
f1e9c8b
refactor(with-storage-sync): remove warning log
mzkmnk Feb 19, 2025
fb7ae07
refactor(with-storage-sync): common returned stub
mzkmnk Feb 19, 2025
55e81df
refactor(indexeddb.service): fixed storeName and dbName,using the Ind…
mzkmnk Feb 19, 2025
d1abdab
refactor(with-storage-sync): withLocalStorage is non-Promised based a…
mzkmnk Feb 20, 2025
c84ffca
Revert "refactor(with-storage-sync): withLocalStorage is non-Promised…
mzkmnk Feb 21, 2025
0d130e7
refactor: mv files
mzkmnk Feb 21, 2025
81f5322
refactor(with-storage-sync): mv file
mzkmnk Feb 21, 2025
07c23ea
refactor(with-storage-sync.spec): mv file
mzkmnk Feb 21, 2025
c041a49
refactor(with-storage-sync): used `withLocalStorage()` returns a sync…
mzkmnk Feb 22, 2025
c45b665
refactor: mv with storage sync
mzkmnk Mar 4, 2025
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
3 changes: 3 additions & 0 deletions apps/demo/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
>Redux Connector</a
>
<a mat-list-item routerLink="/todo-storage-sync">withStorageSync</a>
<a mat-list-item routerLink="/todo-indexeddb-sync"
>withStorageSync(IndexedDB)</a
>
<a mat-list-item routerLink="/reset">withReset</a>
<a mat-list-item routerLink="/immutable-state">withImmutableState</a>
<a mat-list-item routerLink="/feature-factory">withFeatureFactory</a>
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/src/app/devtools/todo-detail.component.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Component, effect, inject, input } from '@angular/core';
import { MatCardModule } from '@angular/material/card';
import { Todo } from './todo-store';
import { patchState, signalStore, withHooks, withState } from '@ngrx/signals';
import {
renameDevtoolsName,
withDevtools,
withGlitchTracking,
withMapper,
} from '@angular-architects/ngrx-toolkit';
import { Todo } from '../shared/todo.service';

/**
* This Store can be instantiated multiple times, if the user
Expand Down
51 changes: 5 additions & 46 deletions apps/demo/src/app/devtools/todo-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,8 @@ import {
withEntities,
} from '@ngrx/signals/entities';
import { updateState, withDevtools } from '@angular-architects/ngrx-toolkit';
import { computed } from '@angular/core';

export interface Todo {
id: number;
name: string;
finished: boolean;
description?: string;
deadline?: Date;
}

export type AddTodo = Omit<Todo, 'id'>;
import { computed, inject } from '@angular/core';
import { Todo, AddTodo, TodoService } from '../shared/todo.service';

export const TodoStore = signalStore(
{ providedIn: 'root' },
Expand Down Expand Up @@ -72,41 +63,9 @@ export const TodoStore = signalStore(
),
})),
withHooks({
onInit: (store) => {
store.add({
name: 'Go for a Walk',
finished: false,
description:
'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.',
});

store.add({
name: 'Read a Book',
finished: false,
description:
'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.',
});

store.add({
name: 'Write a Journal',
finished: false,
description:
'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.',
});

store.add({
name: 'Exercise',
finished: false,
description:
'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.',
});

store.add({
name: 'Cook a Meal',
finished: false,
description:
'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.',
});
onInit: (store, todoService = inject(TodoService)) => {
const todos = todoService.getData();
todos.forEach((todo) => store.add(todo));
},
})
);
3 changes: 2 additions & 1 deletion apps/demo/src/app/devtools/todo.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatIconModule } from '@angular/material/icon';
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { SelectionModel } from '@angular/cdk/collections';
import { Todo, TodoStore } from './todo-store';
import { TodoStore } from './todo-store';
import { TodoDetailComponent } from './todo-detail.component';
import { FormsModule } from '@angular/forms';
import { Todo } from '../shared/todo.service';

@Component({
selector: 'demo-todo',
Expand Down
2 changes: 2 additions & 0 deletions apps/demo/src/app/lazy-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { FlightSearchWithPaginationComponent } from './flight-search-with-pagina
import { FlightSearchReducConnectorComponent } from './flight-search-redux-connector/flight-search.component';
import { provideFlightStore } from './flight-search-redux-connector/+state/redux';
import { TodoComponent } from './devtools/todo.component';
import { TodoIndexeddbSyncComponent } from './todo-indexeddb-sync/todo-indexeddb-sync.component';

export const lazyRoutes: Route[] = [
{ path: 'todo', component: TodoComponent },
Expand All @@ -28,6 +29,7 @@ export const lazyRoutes: Route[] = [
},
{ path: 'flight-edit-dynamic/:id', component: FlightEditDynamicComponent },
{ path: 'todo-storage-sync', component: TodoStorageSyncComponent },
{ path: 'todo-indexeddb-sync', component: TodoIndexeddbSyncComponent },
{
path: 'flight-search-redux-connector',
providers: [provideFlightStore()],
Expand Down
49 changes: 49 additions & 0 deletions apps/demo/src/app/shared/todo.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Injectable } from '@angular/core';

export interface Todo {
id: number;
name: string;
finished: boolean;
description?: string;
deadline?: Date;
}

export type AddTodo = Omit<Todo, 'id'>;

@Injectable({ providedIn: 'root' })
export class TodoService {
getData(): AddTodo[] {
return [
{
name: 'Go for a Walk',
finished: false,
description:
'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.',
},
{
name: 'Read a Book',
finished: false,
description:
'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.',
},
{
name: 'Write a Journal',
finished: false,
description:
'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.',
},
{
name: 'Exercise',
finished: false,
description:
'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.',
},
{
name: 'Cook a Meal',
finished: false,
description:
'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.',
},
];
}
}
54 changes: 54 additions & 0 deletions apps/demo/src/app/todo-indexeddb-sync/synced-todo-store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { getState, patchState, signalStore, withMethods } from '@ngrx/signals';
import {
removeEntity,
setEntity,
updateEntity,
withEntities,
} from '@ngrx/signals/entities';
import { AddTodo, Todo, TodoService } from '../shared/todo.service';
import {
withIndexeddb,
withStorageSync,
} from '@angular-architects/ngrx-toolkit';
import { inject } from '@angular/core';

export const SyncedTodoStore = signalStore(
{ providedIn: 'root' },
withEntities<Todo>(),
withStorageSync(
{
key: 'todos-indexeddb',
},
withIndexeddb()
),
withMethods((store, todoService = inject(TodoService)) => {
let currentId = 0;
return {
add(todo: AddTodo) {
store.readFromStorage();
patchState(store, setEntity({ id: ++currentId, ...todo }));
},

remove(id: number) {
patchState(store, removeEntity(id));
},

toggleFinished(id: number): void {
const todo = store.entityMap()[id];
patchState(
store,
updateEntity({ id, changes: { finished: !todo.finished } })
);
},

reset() {
const state = getState(store);

state.ids.forEach((id) => this.remove(Number(id)));

const todos = todoService.getData();
todos.forEach((todo) => this.add(todo));
},
};
})
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<h2>StorageType:IndexedDB</h2>
<button (click)="onClickReset()" mat-flat-button>reset</button>
<mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<!-- Checkbox Column -->
<ng-container matColumnDef="finished">
<mat-header-cell *matHeaderCellDef></mat-header-cell>
<mat-cell *matCellDef="let row" class="actions">
<mat-checkbox
(change)="checkboxLabel(row)"
(click)="$event.stopPropagation()"
[checked]="row.finished"
>
</mat-checkbox>
<mat-icon (click)="removeTodo(row)">delete</mat-icon>
</mat-cell>
</ng-container>

<!-- Name Column -->
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef>Name</mat-header-cell>
<mat-cell *matCellDef="let element">{{ element.name }}</mat-cell>
</ng-container>

<!-- Description Column -->
<ng-container matColumnDef="description">
<mat-header-cell *matHeaderCellDef>Description</mat-header-cell>
<mat-cell *matCellDef="let element">{{ element.description }}</mat-cell>
</ng-container>

<!-- Deadline Column -->
<ng-container matColumnDef="deadline">
<mat-header-cell *matHeaderCellDef mat-header-cell
>Deadline
</mat-header-cell>
<mat-cell *matCellDef="let element" mat-cell
>{{ element.deadline }}
</mat-cell>
</ng-container>

<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row
(click)="selection.toggle(row)"
*matRowDef="let row; columns: displayedColumns"
></mat-row>
</mat-table>
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Component, effect, inject } from '@angular/core';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatIconModule } from '@angular/material/icon';
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { SyncedTodoStore } from './synced-todo-store';
import { SelectionModel } from '@angular/cdk/collections';
import { Todo } from '../shared/todo.service';
import { MatButton } from '@angular/material/button';

@Component({
selector: 'demo-todo-indexeddb-sync',
imports: [MatCheckboxModule, MatIconModule, MatTableModule, MatButton],
templateUrl: './todo-indexeddb-sync.component.html',
styleUrl: './todo-indexeddb-sync.component.scss',
standalone: true,
})
export class TodoIndexeddbSyncComponent {
todoStore = inject(SyncedTodoStore);

displayedColumns: string[] = ['finished', 'name', 'description', 'deadline'];
dataSource = new MatTableDataSource<Todo>([]);
selection = new SelectionModel<Todo>(true, []);

constructor() {
effect(() => {
this.dataSource.data = this.todoStore.entities();
});
}

checkboxLabel(todo: Todo) {
this.todoStore.toggleFinished(todo.id);
}

removeTodo(todo: Todo) {
this.todoStore.remove(todo.id);
}

onClickReset() {
this.todoStore.reset();
}
}
29 changes: 20 additions & 9 deletions apps/demo/src/app/todo-storage-sync/synced-todo-store.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { patchState, signalStore, withMethods } from '@ngrx/signals';
import { getState, patchState, signalStore, withMethods } from '@ngrx/signals';
import {
withEntities,
setEntity,
removeEntity,
setEntity,
updateEntity,
withEntities,
} from '@ngrx/signals/entities';
import { AddTodo, Todo } from '../devtools/todo-store';
import { withStorageSync } from '@angular-architects/ngrx-toolkit';
import {
withLocalStorage,
withStorageSync,
} from '@angular-architects/ngrx-toolkit';
import { AddTodo, Todo, TodoService } from '../shared/todo.service';
import { inject } from '@angular/core';

export const SyncedTodoStore = signalStore(
{ providedIn: 'root' },
withEntities<Todo>(),
withStorageSync({
key: 'todos',
}),
withMethods((store) => {
withStorageSync('todos', withLocalStorage()),
withMethods((store, todoService = inject(TodoService)) => {
let currentId = 0;
return {
add(todo: AddTodo) {
Expand All @@ -32,6 +34,15 @@ export const SyncedTodoStore = signalStore(
updateEntity({ id, changes: { finished: !todo.finished } })
);
},

reset() {
const state = getState(store);

state.ids.forEach((id) => this.remove(Number(id)));

const todos = todoService.getData();
todos.forEach((todo) => this.add(todo));
},
};
})
);
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<h2>StorageType:LocalStorage</h2>
<button (click)="onClickReset()" mat-flat-button>reset</button>
<mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<!-- Checkbox Column -->
<ng-container matColumnDef="finished">
<mat-header-cell *matHeaderCellDef></mat-header-cell>
<mat-cell *matCellDef="let row" class="actions">
<mat-checkbox
(click)="$event.stopPropagation()"
(change)="checkboxLabel(row)"
(click)="$event.stopPropagation()"
[checked]="row.finished"
>
</mat-checkbox>
Expand All @@ -27,17 +29,17 @@

<!-- Deadline Column -->
<ng-container matColumnDef="deadline">
<mat-header-cell mat-header-cell *matHeaderCellDef
>Deadline</mat-header-cell
>
<mat-cell mat-cell *matCellDef="let element">{{
element.deadline
}}</mat-cell>
<mat-header-cell *matHeaderCellDef mat-header-cell
>Deadline
</mat-header-cell>
<mat-cell *matCellDef="let element" mat-cell
>{{ element.deadline }}
</mat-cell>
</ng-container>

<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row
*matRowDef="let row; columns: displayedColumns"
(click)="selection.toggle(row)"
*matRowDef="let row; columns: displayedColumns"
></mat-row>
</mat-table>
Loading
Loading