Skip to content

Commit f54ba10

Browse files
committed
feat(Answer:30): refactor componen store to use signal store
1 parent 42c4aef commit f54ba10

6 files changed

Lines changed: 218 additions & 211 deletions

File tree

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

Lines changed: 51 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,21 @@
1-
import { AsyncPipe } from '@angular/common';
2-
import { Component, inject, OnInit } from '@angular/core';
3-
import { FormControl, ReactiveFormsModule } from '@angular/forms';
1+
import { Component, inject } from '@angular/core';
2+
import { FormsModule } from '@angular/forms';
43
import { MatFormFieldModule } from '@angular/material/form-field';
54
import { MatInputModule } from '@angular/material/input';
65
import { MatProgressBarModule } from '@angular/material/progress-bar';
76
import { RouterLinkWithHref } from '@angular/router';
8-
import { provideComponentStore } from '@ngrx/component-store';
9-
import {
10-
debounceTime,
11-
distinctUntilChanged,
12-
Observable,
13-
skipWhile,
14-
tap,
15-
} from 'rxjs';
7+
168
import { Photo } from '../photo.model';
17-
import { PhotoStore } from './photos.store';
9+
import { PhotosStore } from './photos.store';
1810

1911
@Component({
2012
selector: 'app-photos',
2113
imports: [
22-
ReactiveFormsModule,
14+
FormsModule,
2315
MatFormFieldModule,
2416
MatProgressBarModule,
2517
MatInputModule,
2618
RouterLinkWithHref,
27-
AsyncPipe,
2819
],
2920
template: `
3021
<h2 class="mb-2 text-xl">Photos</h2>
@@ -34,93 +25,61 @@ import { PhotoStore } from './photos.store';
3425
<input
3526
type="text"
3627
matInput
37-
[formControl]="search"
28+
[ngModel]="store.search()"
29+
(ngModelChange)="store.setSearch($event)"
3830
placeholder="find a photo" />
3931
</mat-form-field>
4032
41-
@let vm = vm$ | async;
42-
<section class="flex flex-col">
43-
<section class="flex items-center gap-3">
44-
<button
45-
[disabled]="vm.page === 1"
46-
[class.bg-gray-400]="vm.page === 1"
47-
class="rounded-md border p-3 text-xl"
48-
(click)="store.previousPage()">
49-
<
50-
</button>
51-
<button
52-
[disabled]="vm.endOfPage"
53-
[class.bg-gray-400]="vm.endOfPage"
54-
class="rounded-md border p-3 text-xl"
55-
(click)="store.nextPage()">
56-
>
57-
</button>
58-
Page :{{ vm.page }} / {{ vm.pages }}
59-
</section>
60-
@if (vm.loading) {
61-
<mat-progress-bar mode="query" class="mt-5"></mat-progress-bar>
62-
}
63-
@if (vm.photos && vm.photos.length > 0) {
64-
<ul class="flex flex-wrap gap-4">
65-
@for (
66-
photo of vm.photos;
67-
track photo.id;
68-
let i = $index
69-
) {
70-
<li>
71-
<a routerLink="detail" [queryParams]="{ photo: encode(photo) }">
72-
<img
73-
src="{{ photo.url_q }}"
74-
alt="{{ photo.title }}"
75-
class="image" />
76-
</a>
77-
</li>
78-
}
79-
</ul>
80-
} @else {
81-
<div>No Photos found. Type a search word.</div>
82-
}
83-
<footer class="text-red-500">
84-
{{ vm.error }}
85-
</footer>
33+
<section class="flex flex-col">
34+
<section class="flex items-center gap-3">
35+
<button
36+
[disabled]="store.page() === 1"
37+
[class.bg-gray-400]="store.page() === 1"
38+
class="rounded-md border p-3 text-xl"
39+
(click)="store.previousPage()">
40+
<
41+
</button>
42+
<button
43+
[disabled]="store.endOfPage()"
44+
[class.bg-gray-400]="store.endOfPage()"
45+
class="rounded-md border p-3 text-xl"
46+
(click)="store.nextPage()">
47+
>
48+
</button>
49+
Page :{{ store.page() }} / {{ store.pages() }}
8650
</section>
51+
@if (store.loading()) {
52+
<mat-progress-bar mode="query" class="mt-5"></mat-progress-bar>
53+
}
54+
@let photos = store.photos();
55+
@if (photos && photos.length > 0) {
56+
<ul class="flex flex-wrap gap-4">
57+
@for (photo of photos; track photo.id; let i = $index) {
58+
<li>
59+
<a routerLink="detail" [queryParams]="{ photo: encode(photo) }">
60+
<img
61+
src="{{ photo.url_q }}"
62+
alt="{{ photo.title }}"
63+
class="image" />
64+
</a>
65+
</li>
66+
}
67+
</ul>
68+
} @else {
69+
<div>No Photos found. Type a search word.</div>
70+
}
71+
<footer class="text-red-500">
72+
{{ store.error() }}
73+
</footer>
74+
</section>
8775
`,
88-
providers: [provideComponentStore(PhotoStore)],
76+
providers: [PhotosStore],
8977
host: {
9078
class: 'p-5 block',
9179
},
9280
})
93-
export default class PhotosComponent implements OnInit {
94-
store = inject(PhotoStore);
95-
readonly vm$: Observable<{
96-
photos: Photo[];
97-
search: string;
98-
page: number;
99-
pages: number;
100-
endOfPage: boolean;
101-
loading: boolean;
102-
error: unknown;
103-
}> = this.store.vm$.pipe(
104-
tap(({ search }) => {
105-
if (!this.formInit) {
106-
this.search.setValue(search);
107-
this.formInit = true;
108-
}
109-
}),
110-
);
111-
112-
private formInit = false;
113-
search = new FormControl();
114-
115-
ngOnInit(): void {
116-
this.store.search(
117-
this.search.valueChanges.pipe(
118-
skipWhile(() => !this.formInit),
119-
debounceTime(300),
120-
distinctUntilChanged(),
121-
),
122-
);
123-
}
81+
export default class PhotosComponent {
82+
readonly store = inject(PhotosStore);
12483

12584
encode(photo: Photo) {
12685
return encodeURIComponent(JSON.stringify(photo));

0 commit comments

Comments
 (0)