From 974110a79576d404443f44c74e251dc56b555ba1 Mon Sep 17 00:00:00 2001 From: michael-small Date: Tue, 12 Aug 2025 01:06:07 +0000 Subject: [PATCH 01/15] chore(demo): pull in reset todo models/data from service --- apps/demo/src/app/reset/todo-store.ts | 53 ++++------------------- apps/demo/src/app/reset/todo.component.ts | 3 +- 2 files changed, 10 insertions(+), 46 deletions(-) diff --git a/apps/demo/src/app/reset/todo-store.ts b/apps/demo/src/app/reset/todo-store.ts index f3b4f1e4..a19fe636 100644 --- a/apps/demo/src/app/reset/todo-store.ts +++ b/apps/demo/src/app/reset/todo-store.ts @@ -1,30 +1,24 @@ import { setResetState, withReset } from '@angular-architects/ngrx-toolkit'; +import { inject } from '@angular/core'; import { getState, patchState, signalStore, withHooks, withMethods, + withProps, withState, } from '@ngrx/signals'; import { addEntity, updateEntity, withEntities } from '@ngrx/signals/entities'; - -export interface Todo { - id: number; - name: string; - finished: boolean; - description?: string; - deadline?: Date; -} - -export type AddTodo = Omit; +import { AddTodo, Todo, TodoService } from '../shared/todo.service'; export const TodoStore = signalStore( { providedIn: 'root' }, + withProps(() => ({ _todoService: inject(TodoService) })), withReset(), withEntities(), - withState({ - selectedIds: [] as number[], + withState<{ selectedIds: number[] }>({ + selectedIds: [], }), withMethods((store) => { let currentId = 0; @@ -43,39 +37,8 @@ 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.', + store._todoService.getData().forEach((todo) => { + store._add(todo); }); setResetState(store, getState(store)); diff --git a/apps/demo/src/app/reset/todo.component.ts b/apps/demo/src/app/reset/todo.component.ts index bb26f491..4b0d3474 100644 --- a/apps/demo/src/app/reset/todo.component.ts +++ b/apps/demo/src/app/reset/todo.component.ts @@ -4,7 +4,8 @@ import { MatButton } from '@angular/material/button'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatIconModule } from '@angular/material/icon'; import { MatTableDataSource, MatTableModule } from '@angular/material/table'; -import { Todo, TodoStore } from './todo-store'; +import { Todo } from '../shared/todo.service'; +import { TodoStore } from './todo-store'; @Component({ template: ` From 1df36851243bba02281357636c2a1e6c2993154a Mon Sep 17 00:00:00 2001 From: michael-small Date: Tue, 12 Aug 2025 01:29:21 +0000 Subject: [PATCH 02/15] chore(demo): make withReset html more declarative --- apps/demo/src/app/reset/todo.component.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/demo/src/app/reset/todo.component.ts b/apps/demo/src/app/reset/todo.component.ts index 4b0d3474..129481e2 100644 --- a/apps/demo/src/app/reset/todo.component.ts +++ b/apps/demo/src/app/reset/todo.component.ts @@ -9,11 +9,13 @@ import { TodoStore } from './todo-store'; @Component({ template: ` -
- -
+

withReset Todo List

-
+ + +
@@ -40,10 +42,10 @@ import { TodoStore } from './todo-store'; (click)="selection.toggle(row)" > -
+ `, styles: ` - .button { + button { margin-bottom: 1em; } `, From 23112d1c824552f211e89d5b0580beefa7ae0a86 Mon Sep 17 00:00:00 2001 From: michael-small Date: Tue, 12 Aug 2025 01:40:40 +0000 Subject: [PATCH 03/15] chore(demo): make app drawer full size, unobscure sidenav --- apps/demo/src/app/app.component.html | 2 +- apps/demo/src/app/app.component.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/demo/src/app/app.component.html b/apps/demo/src/app/app.component.html index a18faa3d..9d1ebd35 100644 --- a/apps/demo/src/app/app.component.html +++ b/apps/demo/src/app/app.component.html @@ -1,7 +1,7 @@ NgRx Toolkit Demo - + DevTools diff --git a/apps/demo/src/app/app.component.ts b/apps/demo/src/app/app.component.ts index 3437c545..fe8abe72 100644 --- a/apps/demo/src/app/app.component.ts +++ b/apps/demo/src/app/app.component.ts @@ -29,12 +29,12 @@ import { RouterLink, RouterOutlet } from '@angular/router'; MatDrawerContent, ], styles: ` - .container { - display: inline; - } .content { margin: 4em; } + mat-drawer-container { + height: 100%; + } `, }) export class AppComponent {} From e04ce26beed53a842b98da1ac9c12287a2ae93a4 Mon Sep 17 00:00:00 2001 From: michael-small Date: Tue, 12 Aug 2025 01:44:55 +0000 Subject: [PATCH 04/15] chore(demo): alphabetize sidebar by feature --- apps/demo/src/app/app.component.html | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/demo/src/app/app.component.html b/apps/demo/src/app/app.component.html index 9d1ebd35..113f5658 100644 --- a/apps/demo/src/app/app.component.html +++ b/apps/demo/src/app/app.component.html @@ -5,19 +5,24 @@ DevTools + Redux Connector withRedux + withConditional + withDataService (Simple) withDataService (Dynamic) + withImmutableState + withFeatureFactory withPagination - Redux Connector + withReset withStorageSync withStorageSync(IndexedDB) Date: Tue, 12 Aug 2025 02:41:52 +0000 Subject: [PATCH 05/15] chore(demo): misc devtools/redux conn/withRedux/condit/simple data Changes overall: - Material fields + buttons etc - Page titles - Some field ordering in classes --- .../src/app/devtools/todo-detail.component.ts | 18 ++--- apps/demo/src/app/devtools/todo.component.ts | 6 +- .../flight-edit-simple.component.html | 39 +++++++---- .../flight-edit-simple.component.ts | 41 ++++++++---- .../flight-search-simple.component.html | 65 ++++++++++++------- .../flight-search-simple.component.ts | 6 +- .../flight-search.component.html | 25 +++++-- .../flight-search.component.ts | 12 +++- .../flight-search.component.html | 3 +- .../flight-search/flight-search.component.ts | 2 +- .../with-conditional/conditional.component.ts | 7 +- 11 files changed, 152 insertions(+), 72 deletions(-) diff --git a/apps/demo/src/app/devtools/todo-detail.component.ts b/apps/demo/src/app/devtools/todo-detail.component.ts index 0234642e..9f3efd7a 100644 --- a/apps/demo/src/app/devtools/todo-detail.component.ts +++ b/apps/demo/src/app/devtools/todo-detail.component.ts @@ -46,14 +46,16 @@ const TodoDetailStore = signalStore( @Component({ selector: 'demo-todo-detail', - template: `
- - {{ todo().name }} - - - - -
`, + template: ` +
+ + {{ todo().name }} + + + + +
+ `, imports: [MatCardModule], providers: [TodoDetailStore], styles: ` diff --git a/apps/demo/src/app/devtools/todo.component.ts b/apps/demo/src/app/devtools/todo.component.ts index abbefcfc..c80f9902 100644 --- a/apps/demo/src/app/devtools/todo.component.ts +++ b/apps/demo/src/app/devtools/todo.component.ts @@ -11,6 +11,8 @@ import { TodoStore } from './todo-store'; @Component({ selector: 'demo-todo', template: ` +

Todo List (DevTools)

+ @@ -51,7 +53,7 @@ import { TodoStore } from './todo-store';
@for (todo of todoStore.selectedTodos(); track todo) { - + }
`, @@ -77,7 +79,7 @@ import { TodoStore } from './todo-store'; export class TodoComponent { todoStore = inject(TodoStore); - displayedColumns: string[] = ['finished', 'name', 'deadline']; + displayedColumns = ['finished', 'name', 'deadline'] as const; dataSource = new MatTableDataSource([]); selection = new SelectionModel(true, []); diff --git a/apps/demo/src/app/flight-search-data-service-simple/flight-edit-simple.component.html b/apps/demo/src/app/flight-search-data-service-simple/flight-edit-simple.component.html index d03ad8de..76548c42 100644 --- a/apps/demo/src/app/flight-search-data-service-simple/flight-edit-simple.component.html +++ b/apps/demo/src/app/flight-search-data-service-simple/flight-edit-simple.component.html @@ -1,4 +1,4 @@ -

Flight Edit (Simple)

+

Flight Edit (Simple)

@if (loading()) {
One moment please ...
@@ -7,18 +7,35 @@

Flight Edit (Simple)

Error: {{ error() }}

} @if (current()) { +
-
- - - - - +
+ + ID: + + + + From: + + + + To: + + + + Date: + + + + Delayed +
-
- - - +
+ + +
} diff --git a/apps/demo/src/app/flight-search-data-service-simple/flight-edit-simple.component.ts b/apps/demo/src/app/flight-search-data-service-simple/flight-edit-simple.component.ts index 4a8177ea..96a132cb 100644 --- a/apps/demo/src/app/flight-search-data-service-simple/flight-edit-simple.component.ts +++ b/apps/demo/src/app/flight-search-data-service-simple/flight-edit-simple.component.ts @@ -1,34 +1,53 @@ -import { CommonModule } from '@angular/common'; -import { Component, Input, OnInit, ViewChild, inject } from '@angular/core'; +import { Component, OnInit, inject, input, viewChild } from '@angular/core'; import { FormsModule, NgForm } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; import { RouterModule } from '@angular/router'; import { Flight } from '../shared/flight'; import { SimpleFlightBookingStore } from './flight-booking-simple.store'; @Component({ - imports: [CommonModule, RouterModule, FormsModule], + imports: [ + RouterModule, + FormsModule, + MatFormFieldModule, + MatInputModule, + MatCheckboxModule, + MatButtonModule, + ], selector: 'demo-flight-edit-simple', templateUrl: './flight-edit-simple.component.html', + styles: ` + #fields { + display: flex; + flex-direction: column; + gap: 10px; + } + #buttons { + display: flex; + gap: 5px; + } + `, }) export class FlightEditSimpleComponent implements OnInit { - @ViewChild(NgForm) - private form!: NgForm; - private store = inject(SimpleFlightBookingStore); + private readonly form = viewChild.required(NgForm); + current = this.store.current; loading = this.store.loading; error = this.store.error; - @Input({ required: true }) - id = ''; + readonly id = input.required(); ngOnInit(): void { - this.store.loadById(this.id); + this.store.loadById(this.id()); } async save() { - const flight = this.form.value as Flight; + const flight = this.form().value as Flight; if (flight.id) { await this.store.update(flight); } else { @@ -41,6 +60,6 @@ export class FlightEditSimpleComponent implements OnInit { } async deleteFlight() { - await this.store.delete(this.form.value); + await this.store.delete(this.form().value); } } diff --git a/apps/demo/src/app/flight-search-data-service-simple/flight-search-simple.component.html b/apps/demo/src/app/flight-search-data-service-simple/flight-search-simple.component.html index 130a6fa7..1a19c89f 100644 --- a/apps/demo/src/app/flight-search-data-service-simple/flight-search-simple.component.html +++ b/apps/demo/src/app/flight-search-data-service-simple/flight-search-simple.component.html @@ -1,17 +1,18 @@

Flight Search (Simple)

-
+
- +
Flight Search (Simple)
- + matInput + /> +
- -
- Loading ... + @if (loading()) { + Loading... + }
-
- - Edit + - -
+ Edit + +
+ }
{{ selected() | json }}
diff --git a/apps/demo/src/app/flight-search-data-service-simple/flight-search-simple.component.ts b/apps/demo/src/app/flight-search-data-service-simple/flight-search-simple.component.ts index 1670419d..89dfd4c6 100644 --- a/apps/demo/src/app/flight-search-data-service-simple/flight-search-simple.component.ts +++ b/apps/demo/src/app/flight-search-data-service-simple/flight-search-simple.component.ts @@ -1,7 +1,8 @@ -import { JsonPipe, NgForOf, NgIf } from '@angular/common'; +import { JsonPipe } from '@angular/common'; import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; +import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatTableModule } from '@angular/material/table'; import { RouterLink } from '@angular/router'; @@ -10,8 +11,6 @@ import { SimpleFlightBookingStore } from './flight-booking-simple.store'; @Component({ imports: [ - NgIf, - NgForOf, JsonPipe, FormsModule, FlightCardComponent, @@ -19,6 +18,7 @@ import { SimpleFlightBookingStore } from './flight-booking-simple.store'; MatInputModule, MatButtonModule, RouterLink, + MatFormFieldModule, ], selector: 'demo-flight-search', templateUrl: './flight-search-simple.component.html', diff --git a/apps/demo/src/app/flight-search-redux-connector/flight-search.component.html b/apps/demo/src/app/flight-search-redux-connector/flight-search.component.html index 2ec452e6..a7d224c0 100644 --- a/apps/demo/src/app/flight-search-redux-connector/flight-search.component.html +++ b/apps/demo/src/app/flight-search-redux-connector/flight-search.component.html @@ -2,27 +2,29 @@

Flight Search (Redux Connector)

- +
- +
@@ -30,12 +32,21 @@

Flight Search (Redux Connector)

(click)="search()" [disabled]="!localState.filter.from() || !localState.filter.to()" class="btn btn-default" + type="button" + mat-raised-button > Search @if (flights().length) { - + }
diff --git a/apps/demo/src/app/flight-search-redux-connector/flight-search.component.ts b/apps/demo/src/app/flight-search-redux-connector/flight-search.component.ts index 32aed7dc..70840e59 100644 --- a/apps/demo/src/app/flight-search-redux-connector/flight-search.component.ts +++ b/apps/demo/src/app/flight-search-redux-connector/flight-search.component.ts @@ -1,6 +1,9 @@ import { JsonPipe } from '@angular/common'; import { Component } from '@angular/core'; import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; import { patchState, signalState } from '@ngrx/signals'; import { Flight } from '../shared/flight'; import { FlightCardComponent } from '../shared/flight-card.component'; @@ -9,7 +12,14 @@ import { ticketActions } from './+state/actions'; import { injectFlightStore } from './+state/redux'; @Component({ - imports: [JsonPipe, FormsModule, FlightCardComponent], + imports: [ + JsonPipe, + FormsModule, + FlightCardComponent, + MatInputModule, + MatButtonModule, + MatFormFieldModule, + ], selector: 'demo-flight-search-redux-connector', templateUrl: './flight-search.component.html', }) diff --git a/apps/demo/src/app/flight-search/flight-search.component.html b/apps/demo/src/app/flight-search/flight-search.component.html index 14156ea2..020263e1 100644 --- a/apps/demo/src/app/flight-search/flight-search.component.html +++ b/apps/demo/src/app/flight-search/flight-search.component.html @@ -1,3 +1,4 @@ +

Flight Search (withRedux)

@@ -13,7 +14,7 @@
- +
diff --git a/apps/demo/src/app/flight-search/flight-search.component.ts b/apps/demo/src/app/flight-search/flight-search.component.ts index 1b134a53..ab07153c 100644 --- a/apps/demo/src/app/flight-search/flight-search.component.ts +++ b/apps/demo/src/app/flight-search/flight-search.component.ts @@ -23,7 +23,7 @@ export class FlightSearchComponent { searchParams: { from: string; to: string } = { from: 'Paris', to: 'London' }; flightStore = inject(FlightStore); - displayedColumns: string[] = ['from', 'to', 'date']; + displayedColumns = ['from', 'to', 'date'] as const; dataSource = new MatTableDataSource([]); selection = new SelectionModel(true, []); diff --git a/apps/demo/src/app/with-conditional/conditional.component.ts b/apps/demo/src/app/with-conditional/conditional.component.ts index ebc80992..2bd7be19 100644 --- a/apps/demo/src/app/with-conditional/conditional.component.ts +++ b/apps/demo/src/app/with-conditional/conditional.component.ts @@ -62,7 +62,7 @@ class ConditionalUserComponent { @Component({ template: `

-
withConditional
+ withConditional

-
@@ -91,12 +91,13 @@ class ConditionalUserComponent { ], }) export class ConditionalSettingComponent { + userService = inject(UserServiceStore); + showUserComponent = signal(false); toggleUserComponent() { this.showUserComponent.update((show) => !show); } - userService = inject(UserServiceStore); protected readonly userFeature = signal<'real' | 'fake'>('real'); effRef = effect(() => { From ed1ae5b6578620e722bb8b9d2ede91d604da96b9 Mon Sep 17 00:00:00 2001 From: michael-small Date: Wed, 13 Aug 2025 01:25:38 +0000 Subject: [PATCH 06/15] chore(demo): run control flow migration --- apps/demo/src/app/app.component.ts | 2 -- .../flight-edit.component.ts | 3 +- .../flight-search.component.html | 31 ++++++++++--------- .../flight-search.component.ts | 11 ++----- .../flight-search-simple.component.html | 9 ++---- .../src/app/shared/flight-card.component.html | 12 +++---- 6 files changed, 29 insertions(+), 39 deletions(-) diff --git a/apps/demo/src/app/app.component.ts b/apps/demo/src/app/app.component.ts index fe8abe72..a5d408fa 100644 --- a/apps/demo/src/app/app.component.ts +++ b/apps/demo/src/app/app.component.ts @@ -1,4 +1,3 @@ -import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatIconModule } from '@angular/material/icon'; @@ -22,7 +21,6 @@ import { RouterLink, RouterOutlet } from '@angular/router'; MatListModule, RouterLink, RouterOutlet, - CommonModule, MatToolbarModule, MatDrawer, MatDrawerContainer, diff --git a/apps/demo/src/app/flight-search-data-service-dynamic/flight-edit.component.ts b/apps/demo/src/app/flight-search-data-service-dynamic/flight-edit.component.ts index 74cb5456..32638334 100644 --- a/apps/demo/src/app/flight-search-data-service-dynamic/flight-edit.component.ts +++ b/apps/demo/src/app/flight-search-data-service-dynamic/flight-edit.component.ts @@ -1,4 +1,3 @@ -import { CommonModule } from '@angular/common'; import { Component, Input, OnInit, ViewChild, inject } from '@angular/core'; import { FormsModule, NgForm } from '@angular/forms'; import { RouterModule } from '@angular/router'; @@ -6,7 +5,7 @@ import { Flight } from '../shared/flight'; import { FlightBookingStore } from './flight-booking.store'; @Component({ - imports: [CommonModule, RouterModule, FormsModule], + imports: [RouterModule, FormsModule], selector: 'demo-flight-edit', templateUrl: './flight-edit.component.html', }) diff --git a/apps/demo/src/app/flight-search-data-service-dynamic/flight-search.component.html b/apps/demo/src/app/flight-search-data-service-dynamic/flight-search.component.html index 39035ade..e9ea8352 100644 --- a/apps/demo/src/app/flight-search-data-service-dynamic/flight-search.component.html +++ b/apps/demo/src/app/flight-search-data-service-dynamic/flight-search.component.html @@ -47,24 +47,27 @@

Flight Search (Dynamic)

- Loading ... + @if (loading()) { + Loading ... + }
-
- - Edit + - -
+ Edit + +
+ }
{{ selected() | json }}
diff --git a/apps/demo/src/app/flight-search-data-service-dynamic/flight-search.component.ts b/apps/demo/src/app/flight-search-data-service-dynamic/flight-search.component.ts index a674e506..173366f6 100644 --- a/apps/demo/src/app/flight-search-data-service-dynamic/flight-search.component.ts +++ b/apps/demo/src/app/flight-search-data-service-dynamic/flight-search.component.ts @@ -1,4 +1,4 @@ -import { JsonPipe, NgForOf, NgIf } from '@angular/common'; +import { JsonPipe } from '@angular/common'; import { Component, inject } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { RouterLink } from '@angular/router'; @@ -6,14 +6,7 @@ import { FlightCardComponent } from '../shared/flight-card.component'; import { FlightBookingStore } from './flight-booking.store'; @Component({ - imports: [ - NgIf, - NgForOf, - JsonPipe, - FormsModule, - FlightCardComponent, - RouterLink, - ], + imports: [JsonPipe, FormsModule, FlightCardComponent, RouterLink], selector: 'demo-flight-search', templateUrl: './flight-search.component.html', }) diff --git a/apps/demo/src/app/flight-search-data-service-simple/flight-search-simple.component.html b/apps/demo/src/app/flight-search-data-service-simple/flight-search-simple.component.html index 1a19c89f..19c81fa7 100644 --- a/apps/demo/src/app/flight-search-data-service-simple/flight-search-simple.component.html +++ b/apps/demo/src/app/flight-search-data-service-simple/flight-search-simple.component.html @@ -15,12 +15,9 @@

Flight Search (Simple)

-
- Invalid city! -
+ @if (form?.controls?.['from']?.hasError('appCity')) { +
Invalid city!
+ }
diff --git a/apps/demo/src/app/shared/flight-card.component.html b/apps/demo/src/app/shared/flight-card.component.html index 0447282b..edacd384 100644 --- a/apps/demo/src/app/shared/flight-card.component.html +++ b/apps/demo/src/app/shared/flight-card.component.html @@ -7,12 +7,12 @@

{{ item.from }} - {{ item.to }}

Flight-No.: #{{ item.id }}

Date: {{ item.date | date: 'dd.MM.yyyy HH:mm:ss' }}

- - + @if (!selected) { + + } + @if (selected) { + + }

From 51d7056b2ec32e7315dced5ba669f851ddc9e38b Mon Sep 17 00:00:00 2001 From: michael-small Date: Wed, 13 Aug 2025 01:30:57 +0000 Subject: [PATCH 07/15] chore(demo): convert to signal inputs --- .../flight-edit.component.ts | 7 +++---- apps/demo/src/app/shared/flight-card.component.html | 12 ++++++------ apps/demo/src/app/shared/flight-card.component.ts | 11 ++++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/demo/src/app/flight-search-data-service-dynamic/flight-edit.component.ts b/apps/demo/src/app/flight-search-data-service-dynamic/flight-edit.component.ts index 32638334..702ac7fd 100644 --- a/apps/demo/src/app/flight-search-data-service-dynamic/flight-edit.component.ts +++ b/apps/demo/src/app/flight-search-data-service-dynamic/flight-edit.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit, ViewChild, inject } from '@angular/core'; +import { Component, OnInit, ViewChild, inject, input } from '@angular/core'; import { FormsModule, NgForm } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { Flight } from '../shared/flight'; @@ -19,11 +19,10 @@ export class FlightEditDynamicComponent implements OnInit { loading = this.store.flightLoading; error = this.store.flightError; - @Input({ required: true }) - id = ''; + readonly id = input.required(); ngOnInit(): void { - this.store.loadFlightById(this.id); + this.store.loadFlightById(this.id()); } async save() { diff --git a/apps/demo/src/app/shared/flight-card.component.html b/apps/demo/src/app/shared/flight-card.component.html index edacd384..a5964352 100644 --- a/apps/demo/src/app/shared/flight-card.component.html +++ b/apps/demo/src/app/shared/flight-card.component.html @@ -1,16 +1,16 @@ -
+
-

{{ item.from }} - {{ item.to }}

+

{{ item().from }} - {{ item().to }}

-

Flight-No.: #{{ item.id }}

-

Date: {{ item.date | date: 'dd.MM.yyyy HH:mm:ss' }}

+

Flight-No.: #{{ item().id }}

+

Date: {{ item().date | date: 'dd.MM.yyyy HH:mm:ss' }}

- @if (!selected) { + @if (!selected()) { } - @if (selected) { + @if (selected()) { } diff --git a/apps/demo/src/app/shared/flight-card.component.ts b/apps/demo/src/app/shared/flight-card.component.ts index 7f27a734..e9392ea4 100644 --- a/apps/demo/src/app/shared/flight-card.component.ts +++ b/apps/demo/src/app/shared/flight-card.component.ts @@ -3,8 +3,9 @@ import { ChangeDetectionStrategy, Component, EventEmitter, - Input, Output, + input, + model, } from '@angular/core'; import { RouterModule } from '@angular/router'; import { initFlight } from './flight'; @@ -16,17 +17,17 @@ import { initFlight } from './flight'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class FlightCardComponent { - @Input() item = initFlight; - @Input() selected: boolean | undefined; + readonly item = input(initFlight); + selected = model.required(); @Output() selectedChange = new EventEmitter(); select() { - this.selected = true; + this.selected.set(true); this.selectedChange.next(true); } deselect() { - this.selected = false; + this.selected.set(false); this.selectedChange.next(false); } } From 9a26893b575204ec42e1d334b7b266d089b1d9f2 Mon Sep 17 00:00:00 2001 From: michael-small Date: Wed, 13 Aug 2025 01:32:01 +0000 Subject: [PATCH 08/15] chore(demo): convert `ngClass` --> `class` --- apps/demo/src/app/shared/flight-card.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/demo/src/app/shared/flight-card.component.html b/apps/demo/src/app/shared/flight-card.component.html index a5964352..4dab20e9 100644 --- a/apps/demo/src/app/shared/flight-card.component.html +++ b/apps/demo/src/app/shared/flight-card.component.html @@ -1,4 +1,4 @@ -

+

{{ item().from }} - {{ item().to }}

From 11602ed50d97a2d57e534963fdc960806cbd673b Mon Sep 17 00:00:00 2001 From: michael-small Date: Wed, 13 Aug 2025 01:36:13 +0000 Subject: [PATCH 09/15] chore(demo): run self closing tag schematic --- apps/demo/src/app/devtools/todo.component.ts | 9 ++++----- .../flight-search.component.html | 3 +-- .../flight-search-with-pagination.component.html | 7 +++---- .../src/app/flight-search/flight-search.component.html | 4 ++-- apps/demo/src/app/reset/todo.component.ts | 9 ++++----- apps/demo/src/app/shared/flight-card.component.html | 2 +- .../todo-indexeddb-sync.component.html | 9 ++++----- .../todo-storage-sync/todo-storage-sync.component.html | 9 ++++----- 8 files changed, 23 insertions(+), 29 deletions(-) diff --git a/apps/demo/src/app/devtools/todo.component.ts b/apps/demo/src/app/devtools/todo.component.ts index c80f9902..0a40d10e 100644 --- a/apps/demo/src/app/devtools/todo.component.ts +++ b/apps/demo/src/app/devtools/todo.component.ts @@ -16,14 +16,13 @@ import { TodoStore } from './todo-store'; - + - + /> delete @@ -44,11 +43,11 @@ import { TodoStore } from './todo-store'; - + + />
diff --git a/apps/demo/src/app/flight-search-redux-connector/flight-search.component.html b/apps/demo/src/app/flight-search-redux-connector/flight-search.component.html index a7d224c0..c6de50e9 100644 --- a/apps/demo/src/app/flight-search-redux-connector/flight-search.component.html +++ b/apps/demo/src/app/flight-search-redux-connector/flight-search.component.html @@ -58,8 +58,7 @@

Flight Search (Redux Connector)

[item]="flight" [selected]="localState.basket()[flight.id]" (selectedChange)="select(flight.id, $event)" - > - + />
}
diff --git a/apps/demo/src/app/flight-search-with-pagination/flight-search-with-pagination.component.html b/apps/demo/src/app/flight-search-with-pagination/flight-search-with-pagination.component.html index 67e78d54..d343c3f8 100644 --- a/apps/demo/src/app/flight-search-with-pagination/flight-search-with-pagination.component.html +++ b/apps/demo/src/app/flight-search-with-pagination/flight-search-with-pagination.component.html @@ -49,11 +49,11 @@

Flight Search (Pagination)

}} - + + /> Flight Search (Pagination) [pageSizeOptions]="[5, 10, 25]" (page)="handlePageEvent($event)" aria-label="Select page" -> - +/> diff --git a/apps/demo/src/app/flight-search/flight-search.component.html b/apps/demo/src/app/flight-search/flight-search.component.html index 020263e1..8f2b57f9 100644 --- a/apps/demo/src/app/flight-search/flight-search.component.html +++ b/apps/demo/src/app/flight-search/flight-search.component.html @@ -38,9 +38,9 @@

Flight Search (withRedux)

}} - + + /> diff --git a/apps/demo/src/app/reset/todo.component.ts b/apps/demo/src/app/reset/todo.component.ts index 129481e2..4fa51d68 100644 --- a/apps/demo/src/app/reset/todo.component.ts +++ b/apps/demo/src/app/reset/todo.component.ts @@ -19,14 +19,13 @@ import { TodoStore } from './todo-store'; - + - + /> @@ -36,11 +35,11 @@ import { TodoStore } from './todo-store'; {{ element.name }} - + + /> `, diff --git a/apps/demo/src/app/shared/flight-card.component.html b/apps/demo/src/app/shared/flight-card.component.html index 4dab20e9..6aa989c3 100644 --- a/apps/demo/src/app/shared/flight-card.component.html +++ b/apps/demo/src/app/shared/flight-card.component.html @@ -13,7 +13,7 @@

{{ item().from }} - {{ item().to }}

@if (selected()) { } - +

diff --git a/apps/demo/src/app/todo-indexeddb-sync/todo-indexeddb-sync.component.html b/apps/demo/src/app/todo-indexeddb-sync/todo-indexeddb-sync.component.html index 5c2d15cf..b13a4f5a 100644 --- a/apps/demo/src/app/todo-indexeddb-sync/todo-indexeddb-sync.component.html +++ b/apps/demo/src/app/todo-indexeddb-sync/todo-indexeddb-sync.component.html @@ -3,14 +3,13 @@

StorageType:IndexedDB

- + - + /> delete @@ -37,9 +36,9 @@

StorageType:IndexedDB

- + + />
diff --git a/apps/demo/src/app/todo-storage-sync/todo-storage-sync.component.html b/apps/demo/src/app/todo-storage-sync/todo-storage-sync.component.html index 7cc3bcff..fc83a0c0 100644 --- a/apps/demo/src/app/todo-storage-sync/todo-storage-sync.component.html +++ b/apps/demo/src/app/todo-storage-sync/todo-storage-sync.component.html @@ -3,14 +3,13 @@

StorageType:LocalStorage

- + - + /> delete @@ -37,9 +36,9 @@

StorageType:LocalStorage

- + + />
From f959c09acb4030e84bfe649b38ef5649ed262657 Mon Sep 17 00:00:00 2001 From: michael-small Date: Wed, 13 Aug 2025 01:37:07 +0000 Subject: [PATCH 10/15] chore(demo): remove uneeded output --- apps/demo/src/app/shared/flight-card.component.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/apps/demo/src/app/shared/flight-card.component.ts b/apps/demo/src/app/shared/flight-card.component.ts index e9392ea4..c6ee0c50 100644 --- a/apps/demo/src/app/shared/flight-card.component.ts +++ b/apps/demo/src/app/shared/flight-card.component.ts @@ -2,8 +2,6 @@ import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, - EventEmitter, - Output, input, model, } from '@angular/core'; @@ -19,15 +17,12 @@ import { initFlight } from './flight'; export class FlightCardComponent { readonly item = input(initFlight); selected = model.required(); - @Output() selectedChange = new EventEmitter(); select() { this.selected.set(true); - this.selectedChange.next(true); } deselect() { this.selected.set(false); - this.selectedChange.next(false); } } From 07650c86831383f415e8fb6fe52559e6b9ddba73 Mon Sep 17 00:00:00 2001 From: michael-small Date: Wed, 13 Aug 2025 01:39:13 +0000 Subject: [PATCH 11/15] chore(demo): use signal view queries --- .../flight-edit.component.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/demo/src/app/flight-search-data-service-dynamic/flight-edit.component.ts b/apps/demo/src/app/flight-search-data-service-dynamic/flight-edit.component.ts index 702ac7fd..04924468 100644 --- a/apps/demo/src/app/flight-search-data-service-dynamic/flight-edit.component.ts +++ b/apps/demo/src/app/flight-search-data-service-dynamic/flight-edit.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, ViewChild, inject, input } from '@angular/core'; +import { Component, OnInit, inject, input, viewChild } from '@angular/core'; import { FormsModule, NgForm } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { Flight } from '../shared/flight'; @@ -10,8 +10,7 @@ import { FlightBookingStore } from './flight-booking.store'; templateUrl: './flight-edit.component.html', }) export class FlightEditDynamicComponent implements OnInit { - @ViewChild(NgForm) - private form!: NgForm; + private readonly form = viewChild.required(NgForm); private store = inject(FlightBookingStore); @@ -26,7 +25,7 @@ export class FlightEditDynamicComponent implements OnInit { } async save() { - const flight = this.form.value as Flight; + const flight = this.form().value as Flight; if (flight.id) { await this.store.updateFlight(flight); } else { @@ -39,6 +38,6 @@ export class FlightEditDynamicComponent implements OnInit { } async deleteFlight() { - await this.store.deleteFlight(this.form.value); + await this.store.deleteFlight(this.form().value); } } From 19ac2623fe68804a19c759eaa461d5483d9b9343 Mon Sep 17 00:00:00 2001 From: michael-small Date: Wed, 13 Aug 2025 01:40:42 +0000 Subject: [PATCH 12/15] chore(app): shrink `CommonModule` usage to exact submodule use --- apps/demo/src/app/core/sidebar/sidebar.component.ts | 4 ++-- apps/demo/src/app/shared/flight-card.component.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/demo/src/app/core/sidebar/sidebar.component.ts b/apps/demo/src/app/core/sidebar/sidebar.component.ts index 25034ee8..c3f3a92d 100644 --- a/apps/demo/src/app/core/sidebar/sidebar.component.ts +++ b/apps/demo/src/app/core/sidebar/sidebar.component.ts @@ -1,5 +1,5 @@ import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; -import { CommonModule } from '@angular/common'; +import { AsyncPipe } from '@angular/common'; import { Component, inject } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; @@ -13,12 +13,12 @@ import { map, shareReplay } from 'rxjs'; selector: 'demo-sidebar-cmp', imports: [ RouterModule, - CommonModule, MatToolbarModule, MatButtonModule, MatSidenavModule, MatIconModule, MatListModule, + AsyncPipe, ], templateUrl: './sidebar.component.html', styleUrls: ['./sidebar.component.css'], diff --git a/apps/demo/src/app/shared/flight-card.component.ts b/apps/demo/src/app/shared/flight-card.component.ts index c6ee0c50..52768bb9 100644 --- a/apps/demo/src/app/shared/flight-card.component.ts +++ b/apps/demo/src/app/shared/flight-card.component.ts @@ -1,4 +1,4 @@ -import { CommonModule } from '@angular/common'; +import { DatePipe } from '@angular/common'; import { ChangeDetectionStrategy, Component, @@ -10,7 +10,7 @@ import { initFlight } from './flight'; @Component({ selector: 'demo-flight-card', - imports: [CommonModule, RouterModule], + imports: [RouterModule, DatePipe], templateUrl: './flight-card.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) From 7fe3f4e1791bc1d5f8550d182cd0de711e5cbdc2 Mon Sep 17 00:00:00 2001 From: michael-small Date: Wed, 13 Aug 2025 01:46:54 +0000 Subject: [PATCH 13/15] chore(demo): remove unused SCSS files --- .../app/todo-indexeddb-sync/todo-indexeddb-sync.component.scss | 0 .../src/app/todo-indexeddb-sync/todo-indexeddb-sync.component.ts | 1 - .../src/app/todo-storage-sync/todo-storage-sync.component.scss | 0 .../src/app/todo-storage-sync/todo-storage-sync.component.ts | 1 - 4 files changed, 2 deletions(-) delete mode 100644 apps/demo/src/app/todo-indexeddb-sync/todo-indexeddb-sync.component.scss delete mode 100644 apps/demo/src/app/todo-storage-sync/todo-storage-sync.component.scss diff --git a/apps/demo/src/app/todo-indexeddb-sync/todo-indexeddb-sync.component.scss b/apps/demo/src/app/todo-indexeddb-sync/todo-indexeddb-sync.component.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/demo/src/app/todo-indexeddb-sync/todo-indexeddb-sync.component.ts b/apps/demo/src/app/todo-indexeddb-sync/todo-indexeddb-sync.component.ts index dee90768..93de808c 100644 --- a/apps/demo/src/app/todo-indexeddb-sync/todo-indexeddb-sync.component.ts +++ b/apps/demo/src/app/todo-indexeddb-sync/todo-indexeddb-sync.component.ts @@ -11,7 +11,6 @@ import { SyncedTodoStore } from './synced-todo-store'; 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 { diff --git a/apps/demo/src/app/todo-storage-sync/todo-storage-sync.component.scss b/apps/demo/src/app/todo-storage-sync/todo-storage-sync.component.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/demo/src/app/todo-storage-sync/todo-storage-sync.component.ts b/apps/demo/src/app/todo-storage-sync/todo-storage-sync.component.ts index 96286165..404613b3 100644 --- a/apps/demo/src/app/todo-storage-sync/todo-storage-sync.component.ts +++ b/apps/demo/src/app/todo-storage-sync/todo-storage-sync.component.ts @@ -11,7 +11,6 @@ import { SyncedTodoStore } from './synced-todo-store'; selector: 'demo-todo-storage-sync', imports: [MatCheckboxModule, MatIconModule, MatTableModule, MatButton], templateUrl: './todo-storage-sync.component.html', - styleUrl: './todo-storage-sync.component.scss', }) export class TodoStorageSyncComponent { todoStore = inject(SyncedTodoStore); From a7e7440336f35252e80d7bbf229a1836d9d412b6 Mon Sep 17 00:00:00 2001 From: michael-small Date: Wed, 13 Aug 2025 01:48:44 +0000 Subject: [PATCH 14/15] chore(demo): narrow string literal types with `as const` --- .../flight-search-with-pagination.component.ts | 2 +- apps/demo/src/app/reset/todo.component.ts | 2 +- .../app/todo-indexeddb-sync/todo-indexeddb-sync.component.ts | 2 +- .../src/app/todo-storage-sync/todo-storage-sync.component.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/demo/src/app/flight-search-with-pagination/flight-search-with-pagination.component.ts b/apps/demo/src/app/flight-search-with-pagination/flight-search-with-pagination.component.ts index c96574b4..8f8231f8 100644 --- a/apps/demo/src/app/flight-search-with-pagination/flight-search-with-pagination.component.ts +++ b/apps/demo/src/app/flight-search-with-pagination/flight-search-with-pagination.component.ts @@ -26,7 +26,7 @@ export class FlightSearchWithPaginationComponent { searchParams: { from: string; to: string } = { from: 'Wien', to: '' }; flightStore = inject(FlightBookingStore); - displayedColumns: string[] = ['from', 'to', 'date']; + displayedColumns = ['from', 'to', 'date'] as const; dataSource = new MatTableDataSource([]); selection = new SelectionModel(true, []); diff --git a/apps/demo/src/app/reset/todo.component.ts b/apps/demo/src/app/reset/todo.component.ts index 4fa51d68..fad5eb0b 100644 --- a/apps/demo/src/app/reset/todo.component.ts +++ b/apps/demo/src/app/reset/todo.component.ts @@ -53,7 +53,7 @@ import { TodoStore } from './todo-store'; export class TodoComponent { todoStore = inject(TodoStore); - displayedColumns: string[] = ['finished', 'name']; + displayedColumns = ['finished', 'name'] as const; dataSource = new MatTableDataSource([]); selection = new SelectionModel(true, []); diff --git a/apps/demo/src/app/todo-indexeddb-sync/todo-indexeddb-sync.component.ts b/apps/demo/src/app/todo-indexeddb-sync/todo-indexeddb-sync.component.ts index 93de808c..0db6cfbf 100644 --- a/apps/demo/src/app/todo-indexeddb-sync/todo-indexeddb-sync.component.ts +++ b/apps/demo/src/app/todo-indexeddb-sync/todo-indexeddb-sync.component.ts @@ -16,7 +16,7 @@ import { SyncedTodoStore } from './synced-todo-store'; export class TodoIndexeddbSyncComponent { todoStore = inject(SyncedTodoStore); - displayedColumns: string[] = ['finished', 'name', 'description', 'deadline']; + displayedColumns = ['finished', 'name', 'description', 'deadline'] as const; dataSource = new MatTableDataSource([]); selection = new SelectionModel(true, []); diff --git a/apps/demo/src/app/todo-storage-sync/todo-storage-sync.component.ts b/apps/demo/src/app/todo-storage-sync/todo-storage-sync.component.ts index 404613b3..f7332325 100644 --- a/apps/demo/src/app/todo-storage-sync/todo-storage-sync.component.ts +++ b/apps/demo/src/app/todo-storage-sync/todo-storage-sync.component.ts @@ -15,7 +15,7 @@ import { SyncedTodoStore } from './synced-todo-store'; export class TodoStorageSyncComponent { todoStore = inject(SyncedTodoStore); - displayedColumns: string[] = ['finished', 'name', 'description', 'deadline']; + displayedColumns = ['finished', 'name', 'description', 'deadline'] as const; dataSource = new MatTableDataSource([]); selection = new SelectionModel(true, []); From a8bdc29b48dc8414097e29def6294c646d7aadc6 Mon Sep 17 00:00:00 2001 From: Michael Small Date: Wed, 17 Sep 2025 19:05:14 -0500 Subject: [PATCH 15/15] chore(demo): get rid of excess merged in links --- apps/demo/src/app/app.component.html | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/demo/src/app/app.component.html b/apps/demo/src/app/app.component.html index 113f5658..2f2574af 100644 --- a/apps/demo/src/app/app.component.html +++ b/apps/demo/src/app/app.component.html @@ -22,15 +22,12 @@ withPagination - withReset withStorageSync withStorageSync(IndexedDB) withReset - withImmutableState - withFeatureFactory - withConditional + withMutation rxMutation (without Store)