Skip to content

Commit 882bf93

Browse files
author
Hamza Hanfi
committed
feat: install and setup of singalStore
1 parent b84e56c commit 882bf93

4 files changed

Lines changed: 116 additions & 33 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { computed, effect } from '@angular/core';
2+
import {
3+
patchState,
4+
signalStore,
5+
withComputed,
6+
withHooks,
7+
withMethods,
8+
withState,
9+
} from '@ngrx/signals';
10+
import { Photo } from '../photo.model';
11+
12+
export interface PhotoState {
13+
photos: Photo[];
14+
search: string;
15+
page: number;
16+
pages: number;
17+
loading: boolean;
18+
error: unknown;
19+
}
20+
21+
const initialState: PhotoState = {
22+
photos: [],
23+
search: '',
24+
page: 1,
25+
pages: 1,
26+
loading: false,
27+
error: '',
28+
};
29+
30+
const PHOTO_STATE_KEY = 'photo_search';
31+
32+
export const PhotoSignalStore = signalStore(
33+
withState(initialState),
34+
withComputed((store) => ({
35+
endOfPage: computed(() => store.page() === store.pages()),
36+
})),
37+
withMethods((store) => {
38+
return {
39+
nextPage() {
40+
patchState(store, {
41+
page: store.page() + 1,
42+
});
43+
},
44+
previousPage() {
45+
patchState(store, {
46+
page: store.page() - 1,
47+
});
48+
},
49+
setSearch(search: string) {
50+
patchState(store, { search, page: 1 });
51+
},
52+
};
53+
}),
54+
withHooks({
55+
onInit(store) {
56+
const savedJSONState = localStorage.getItem(PHOTO_STATE_KEY);
57+
savedJSONState === null
58+
? patchState(store)
59+
: patchState(store, {
60+
search: JSON.parse(savedJSONState).search,
61+
page: JSON.parse(savedJSONState).page,
62+
});
63+
effect(() => {
64+
console.log('search/page', store.search(), store.page());
65+
});
66+
},
67+
}),
68+
);

apps/angular/interop-rxjs-signal/src/app/list/photos.component.ts

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import { NgFor, NgIf } from '@angular/common';
2-
import { Component, OnInit, inject } from '@angular/core';
2+
import { Component, DestroyRef, OnInit, inject } from '@angular/core';
3+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
34
import { FormControl, ReactiveFormsModule } from '@angular/forms';
45
import { MatFormFieldModule } from '@angular/material/form-field';
56
import { MatInputModule } from '@angular/material/input';
67
import { MatProgressBarModule } from '@angular/material/progress-bar';
78
import { RouterLinkWithHref } from '@angular/router';
8-
import { LetDirective } from '@ngrx/component';
99
import { provideComponentStore } from '@ngrx/component-store';
10-
import { debounceTime, distinctUntilChanged, skipWhile, tap } from 'rxjs';
10+
import { debounceTime, distinctUntilChanged, filter } from 'rxjs';
1111
import { Photo } from '../photo.model';
12+
import { PhotoSignalStore } from './photo-signal.store';
1213
import { PhotoStore } from './photos.store';
1314

1415
@Component({
@@ -21,7 +22,6 @@ import { PhotoStore } from './photos.store';
2122
NgIf,
2223
NgFor,
2324
MatInputModule,
24-
LetDirective,
2525
RouterLinkWithHref,
2626
],
2727
template: `
@@ -36,33 +36,36 @@ import { PhotoStore } from './photos.store';
3636
placeholder="find a photo" />
3737
</mat-form-field>
3838
39-
<ng-container *ngrxLet="vm$ as vm">
39+
<ng-container>
4040
<section class="flex flex-col">
4141
<section class="flex items-center gap-3">
4242
<button
43-
[disabled]="vm.page === 1"
44-
[class.bg-gray-400]="vm.page === 1"
43+
[disabled]="signalStore.page() === 1"
44+
[class.bg-gray-400]="signalStore.page() === 1"
4545
class="rounded-md border p-3 text-xl"
46-
(click)="store.previousPage()">
46+
(click)="signalStore.previousPage()">
4747
<
4848
</button>
4949
<button
50-
[disabled]="vm.endOfPage"
51-
[class.bg-gray-400]="vm.endOfPage"
50+
[disabled]="signalStore.endOfPage()"
51+
[class.bg-gray-400]="signalStore.endOfPage()"
5252
class="rounded-md border p-3 text-xl"
53-
(click)="store.nextPage()">
53+
(click)="signalStore.nextPage()">
5454
>
5555
</button>
56-
Page :{{ vm.page }} / {{ vm.pages }}
56+
Page :{{ signalStore.page() }} / {{ signalStore.pages() }}
5757
</section>
5858
<mat-progress-bar
5959
mode="query"
60-
*ngIf="vm.loading"
60+
*ngIf="signalStore.loading()"
6161
class="mt-5"></mat-progress-bar>
6262
<ul
6363
class="flex flex-wrap gap-4"
64-
*ngIf="vm.photos && vm.photos.length > 0; else noPhoto">
65-
<li *ngFor="let photo of vm.photos; trackBy: trackById">
64+
*ngIf="
65+
signalStore.photos() && signalStore.photos().length > 0;
66+
else noPhoto
67+
">
68+
<li *ngFor="let photo of signalStore.photos(); trackBy: trackById">
6669
<a routerLink="detail" [queryParams]="{ photo: encode(photo) }">
6770
<img
6871
src="{{ photo.url_q }}"
@@ -75,38 +78,30 @@ import { PhotoStore } from './photos.store';
7578
<div>No Photos found. Type a search word.</div>
7679
</ng-template>
7780
<footer class="text-red-500">
78-
{{ vm.error }}
81+
{{ signalStore.error() }}
7982
</footer>
8083
</section>
8184
</ng-container>
8285
`,
83-
providers: [provideComponentStore(PhotoStore)],
86+
providers: [provideComponentStore(PhotoStore), PhotoSignalStore],
8487
host: {
8588
class: 'p-5 block',
8689
},
8790
})
8891
export default class PhotosComponent implements OnInit {
89-
store = inject(PhotoStore);
90-
readonly vm$ = this.store.vm$.pipe(
91-
tap(({ search }) => {
92-
if (!this.formInit) {
93-
this.search.setValue(search);
94-
this.formInit = true;
95-
}
96-
}),
97-
);
98-
99-
private formInit = false;
92+
readonly signalStore = inject(PhotoSignalStore);
93+
private readonly destroyRef = inject(DestroyRef);
10094
search = new FormControl();
10195

10296
ngOnInit(): void {
103-
this.store.search(
104-
this.search.valueChanges.pipe(
105-
skipWhile(() => !this.formInit),
97+
this.search.valueChanges
98+
.pipe(
10699
debounceTime(300),
107100
distinctUntilChanged(),
108-
),
109-
);
101+
filter((value) => value.length >= 3),
102+
takeUntilDestroyed(this.destroyRef),
103+
)
104+
.subscribe((searchValue) => this.signalStore.setSearch(searchValue));
110105
}
111106

112107
trackById(index: number, photo: Photo) {

package-lock.json

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"@ngrx/effects": "17.0.1",
2929
"@ngrx/entity": "17.0.1",
3030
"@ngrx/router-store": "17.0.1",
31+
"@ngrx/signals": "^17.2.0",
3132
"@ngrx/store": "17.0.1",
3233
"@nx/angular": "17.2.8",
3334
"@rx-angular/cdk": "^17.0.0",

0 commit comments

Comments
 (0)