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' ;
43import { MatFormFieldModule } from '@angular/material/form-field' ;
54import { MatInputModule } from '@angular/material/input' ;
65import { MatProgressBarModule } from '@angular/material/progress-bar' ;
76import { 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+
168import { 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