Skip to content

Commit c9f4ed3

Browse files
committed
refactor: migrate to signals for checklist
1 parent 04bd01d commit c9f4ed3

17 files changed

+186
-288
lines changed

src/app/checklist/checklist-cta-bar/checklist-cta-bar.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
</button>
1515
</div>
1616
<div class="cta-buttons" *ngIf="showActionButtons">
17-
<button mat-button (click)="checkAll.emit()"><mat-icon>done_all</mat-icon></button>
18-
<button mat-button (click)="uncheckAll.emit()"><mat-icon>filter_none</mat-icon></button>
17+
<button mat-button (click)="checkAll.emit()" matTooltip="Check All"><mat-icon>done_all</mat-icon></button>
18+
<button mat-button (click)="uncheckAll.emit()" matTooltip="Uncheck All"><mat-icon>filter_none</mat-icon></button>
1919
</div>
2020
</div>

src/app/checklist/checklist-cta-bar/checklist-cta-bar.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ import { ChecklistFilter } from '../models/checklist.model';
33
import { MatIcon } from '@angular/material/icon';
44
import { MatButton } from '@angular/material/button';
55
import { NgIf } from '@angular/common';
6+
import { MatTooltip } from '@angular/material/tooltip';
67

78
@Component({
89
standalone: true,
910
selector: 'ac-checklist-cta-bar',
1011
templateUrl: './checklist-cta-bar.component.html',
1112
styleUrls: ['./checklist-cta-bar.component.scss'],
12-
imports: [NgIf, MatButton, MatIcon]
13+
imports: [NgIf, MatButton, MatIcon, MatTooltip]
1314
})
1415
export class ChecklistCtaBarComponent {
1516
@Input() filter: ChecklistFilter;

src/app/checklist/checklist-detail-view/checklist-detail-view.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<ng-container *ngIf="item$ | async as item">
1+
<ng-container *ngIf="item() as item">
22
<header>
33
<mat-checkbox color="primary" (click)="toggleItem(item)" [checked]="item.checked">Done</mat-checkbox>
44
<ac-checklist-favorite-button

src/app/checklist/checklist-detail-view/checklist-detail-view.component.ts

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { Component, OnInit } from '@angular/core';
2-
import { select, Store } from '@ngrx/store';
3-
import { Observable } from 'rxjs';
1+
import { Component, inject } from '@angular/core';
2+
import { Store } from '@ngrx/store';
43
import { ToggleFavorite, ToggleItem } from '../../projects/state/projects.actions';
54
import { ApplicationState } from '../../state/app.state';
65
import { ChecklistItem } from '../models/checklist.model';
@@ -9,23 +8,18 @@ import { BannerComponent } from '../../shared/banner/banner.component';
98
import { ChecklistMetadataComponent } from '../checklist-item-metadata/checklist-metadata.component';
109
import { ChecklistFavoriteButtonComponent } from '../checklist-favorite-button/checklist-favorite-button.component';
1110
import { MatCheckbox } from '@angular/material/checkbox';
12-
import { NgIf, AsyncPipe } from '@angular/common';
11+
import { NgIf } from '@angular/common';
1312

1413
@Component({
1514
standalone: true,
1615
selector: 'ac-checklist-detail-view',
1716
templateUrl: './checklist-detail-view.component.html',
1817
styleUrls: ['./checklist-detail-view.component.scss'],
19-
imports: [NgIf, MatCheckbox, ChecklistFavoriteButtonComponent, ChecklistMetadataComponent, BannerComponent, AsyncPipe]
18+
imports: [NgIf, MatCheckbox, ChecklistFavoriteButtonComponent, ChecklistMetadataComponent, BannerComponent]
2019
})
21-
export class ChecklistDetailViewComponent implements OnInit {
22-
item$: Observable<any>;
23-
24-
constructor(private store: Store<ApplicationState>) {}
25-
26-
ngOnInit() {
27-
this.item$ = this.store.pipe(select(ChecklistSelectors.getSelectedItem));
28-
}
20+
export class ChecklistDetailViewComponent {
21+
private store = inject<Store<ApplicationState>>(Store);
22+
item = this.store.selectSignal<any>(ChecklistSelectors.getSelectedItem);
2923

3024
toggleItem(item: ChecklistItem) {
3125
this.store.dispatch(new ToggleItem(item));

src/app/checklist/checklist-favorites-view/checklist-favorites-view.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<header>Favorites</header>
22

3-
<ac-checklist-cta-bar [filter]="filter$ | async" [showActionButtons]="false" (filterChange)="setFilter($event)">
3+
<ac-checklist-cta-bar [filter]="filter()" [showActionButtons]="false" (filterChange)="setFilter($event)">
44
</ac-checklist-cta-bar>
55

6-
<ng-container *ngIf="favorites$ | async as favorites">
6+
<ng-container *ngIf="favorites() as favorites">
77
<ng-container *ngIf="favorites.length; else noFavorites">
88
<ul class="category" *ngFor="let favorite of favorites; trackBy: trackByCategoryTitle">
99
<h4>{{ favorite.category.title }}</h4>

src/app/checklist/checklist-favorites-view/checklist-favorites-view.component.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,26 @@
1-
import { Component, OnInit } from '@angular/core';
2-
import { select, Store } from '@ngrx/store';
3-
import { Observable } from 'rxjs';
1+
import { Component, inject } from '@angular/core';
2+
import { Store } from '@ngrx/store';
43
import { ToggleFavorite, ToggleItem } from '../../projects/state/projects.actions';
54
import { ApplicationState } from '../../state/app.state';
65
import { ChecklistFilter, ChecklistItem, Favorite } from '../models/checklist.model';
76
import { SetFavoritesFilter } from '../state/checklist.actions';
87
import { ChecklistSelectors } from '../state/checklist.selectors';
98
import { ChecklistListItemComponent } from '../checklist-list/checklist-list-item.component';
109
import { ChecklistListComponent } from '../checklist-list/checklist-list.component';
11-
import { NgIf, NgFor, AsyncPipe } from '@angular/common';
10+
import { NgIf, NgFor } from '@angular/common';
1211
import { ChecklistCtaBarComponent } from '../checklist-cta-bar/checklist-cta-bar.component';
1312

1413
@Component({
1514
standalone: true,
1615
selector: 'ac-checklist-favorites-view',
1716
templateUrl: './checklist-favorites-view.component.html',
1817
styleUrls: ['./checklist-favorites-view.component.scss'],
19-
imports: [ChecklistCtaBarComponent, NgIf, NgFor, ChecklistListComponent, ChecklistListItemComponent, AsyncPipe]
18+
imports: [ChecklistCtaBarComponent, NgIf, NgFor, ChecklistListComponent, ChecklistListItemComponent]
2019
})
21-
export class ChecklistFavoritesViewComponent implements OnInit {
22-
favorites$: Observable<Array<Favorite>>;
23-
filter$: Observable<ChecklistFilter>;
24-
25-
constructor(private store: Store<ApplicationState>) {}
26-
27-
ngOnInit() {
28-
this.favorites$ = this.store.pipe(select(ChecklistSelectors.getFilteredFavorites));
29-
this.filter$ = this.store.pipe(select(ChecklistSelectors.getFavoritesFilter));
30-
}
20+
export class ChecklistFavoritesViewComponent {
21+
private store = inject<Store<ApplicationState>>(Store);
22+
favorites = this.store.selectSignal(ChecklistSelectors.getFilteredFavorites);
23+
filter = this.store.selectSignal(ChecklistSelectors.getFavoritesFilter);
3124

3225
setFilter(filter: ChecklistFilter) {
3326
this.store.dispatch(new SetFavoritesFilter(filter));

src/app/checklist/checklist-list-view/checklist-list-view.component.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<ac-checklist-cta-bar
2-
[filter]="filter$ | async"
3-
[showActionButtons]="showActionButtons$ | async"
2+
[filter]="filter()"
3+
[showActionButtons]="showActionButtons()"
44
(filterChange)="setFilter($event)"
55
(checkAll)="checkAllItems()"
66
(uncheckAll)="uncheckAllItems()"
@@ -9,7 +9,7 @@
99

1010
<ac-checklist-list class="checklist-items">
1111
<ac-checklist-list-item
12-
*ngFor="let item of items$ | async; trackBy: trackById"
12+
*ngFor="let item of items(); trackBy: trackById"
1313
[item]="item"
1414
(toggleItem)="toggleItem($event)"
1515
(toggleFavorite)="toggleFavorite($event)"
Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import { Component, OnInit } from '@angular/core';
2-
import { select, Store } from '@ngrx/store';
3-
import { combineLatest, Observable } from 'rxjs';
1+
import { Component, Signal, computed, inject } from '@angular/core';
2+
import { Store } from '@ngrx/store';
43
import { CheckAll, ToggleFavorite, ToggleItem, UncheckAll } from '../../projects/state/projects.actions';
54
import { BreakpointService } from '../../shared/breakpoint.service';
6-
import { selectOnce } from '../../shared/operators';
75
import { ApplicationState } from '../../state/app.state';
86
import { CategoryEntity, ChecklistFilter, ChecklistItem } from '../models/checklist.model';
97
import { SetCategoriesFilter } from '../state/checklist.actions';
108
import { ChecklistSelectors } from '../state/checklist.selectors';
119
import { ChecklistListItemComponent } from '../checklist-list/checklist-list-item.component';
12-
import { NgFor, AsyncPipe } from '@angular/common';
10+
import { NgFor } from '@angular/common';
1311
import { ChecklistListComponent } from '../checklist-list/checklist-list.component';
1412
import { ChecklistCtaBarComponent } from '../checklist-cta-bar/checklist-cta-bar.component';
1513

@@ -18,22 +16,14 @@ import { ChecklistCtaBarComponent } from '../checklist-cta-bar/checklist-cta-bar
1816
selector: 'ac-list-view',
1917
templateUrl: './checklist-list-view.component.html',
2018
styleUrls: ['./checklist-list-view.component.scss'],
21-
imports: [ChecklistCtaBarComponent, ChecklistListComponent, NgFor, ChecklistListItemComponent, AsyncPipe]
19+
imports: [ChecklistCtaBarComponent, ChecklistListComponent, NgFor, ChecklistListItemComponent]
2220
})
23-
export class ListViewComponent implements OnInit {
24-
items$: Observable<any>;
25-
filter$: Observable<ChecklistFilter>;
26-
showActionButtons$: Observable<boolean>;
27-
28-
constructor(private store: Store<ApplicationState>, private breakpointService: BreakpointService) {}
29-
30-
ngOnInit() {
31-
this.items$ = this.store.pipe(select(ChecklistSelectors.getItemsFromSelectedCategory));
32-
this.filter$ = this.store.pipe(select(ChecklistSelectors.getCategoriesFilter));
33-
34-
const { medium$, desktop$ } = this.breakpointService.getAllBreakpoints();
35-
this.showActionButtons$ = combineLatest(medium$, desktop$, (medium, desktop) => medium || desktop);
36-
}
21+
export class ListViewComponent {
22+
private store = inject<Store<ApplicationState>>(Store);
23+
private breakpointService = inject(BreakpointService);
24+
items = this.store.selectSignal(ChecklistSelectors.getItemsFromSelectedCategory);
25+
filter = this.store.selectSignal(ChecklistSelectors.getCategoriesFilter);
26+
showActionButtons = computed(() => this.breakpointService.medium() || this.breakpointService.desktop());
3727

3828
toggleItem(item: ChecklistItem) {
3929
this.store.dispatch(new ToggleItem(item));
@@ -44,11 +34,13 @@ export class ListViewComponent implements OnInit {
4434
}
4535

4636
checkAllItems() {
47-
this.getSelectedCategory().subscribe(category => this.store.dispatch(new CheckAll(category)));
37+
const categories = this.getSelectedCategory();
38+
this.store.dispatch(new CheckAll(categories));
4839
}
4940

5041
uncheckAllItems() {
51-
this.getSelectedCategory().subscribe(category => this.store.dispatch(new UncheckAll(category)));
42+
const categories = this.getSelectedCategory();
43+
this.store.dispatch(new UncheckAll(categories));
5244
}
5345

5446
toggleFavorite(item: ChecklistItem) {
@@ -59,7 +51,7 @@ export class ListViewComponent implements OnInit {
5951
return item.id;
6052
}
6153

62-
private getSelectedCategory(): Observable<CategoryEntity> {
63-
return this.store.pipe(selectOnce(ChecklistSelectors.getSelectedCategory));
54+
private get getSelectedCategory(): Signal<CategoryEntity> {
55+
return this.store.selectSignal(ChecklistSelectors.getSelectedCategory);
6456
}
6557
}

src/app/checklist/checklist-overview/checklist-overview.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<ul [@breadcrumb]="breadcrumb.length" class="breadcrumb" *ngIf="breadcrumb$ | async as breadcrumb">
1+
<ul [@breadcrumb]="breadcrumb.length" class="breadcrumb" *ngIf="breadcrumbs() as breadcrumb">
22
<ng-container *ngFor="let item of breadcrumb; let last = last; trackBy: trackByTitle">
33
<li (click)="goBack(last)" [class.active]="last" class="breadcrumb-item">{{ item.title }}</li>
44
<li class="breadcrumb-item-separator" *ngIf="!last"><mat-icon>chevron_right</mat-icon></li>
Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,21 @@
11
import { animate, query, stagger, style, transition, trigger } from '@angular/animations';
2-
import { Component, OnInit } from '@angular/core';
2+
import { Component, effect, inject, untracked } from '@angular/core';
33
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
4-
import { select, Store } from '@ngrx/store';
5-
import { Observable, zip } from 'rxjs';
6-
import { filter, switchMap, tap } from 'rxjs/operators';
7-
import { selectOnce } from '../../shared/operators';
4+
import { Store } from '@ngrx/store';
85
import { extractRouteParams, getActivatedChild } from '../../shared/router.utils';
96
import { ApplicationState } from '../../state/app.state';
10-
import { Category, ChecklistItem } from '../models/checklist.model';
7+
import { BreadcrumbItem } from '../models/checklist.model';
118
import { ChecklistSelectors } from '../state/checklist.selectors';
129
import { MatIcon } from '@angular/material/icon';
13-
import { NgIf, NgFor, AsyncPipe } from '@angular/common';
10+
import { NgIf, NgFor } from '@angular/common';
11+
import { toSignal } from '@angular/core/rxjs-interop';
1412

1513
@Component({
1614
standalone: true,
1715
selector: 'ac-checklist-overview',
1816
templateUrl: './checklist-overview.component.html',
1917
styleUrls: ['./checklist-overview.component.scss'],
20-
imports: [NgIf, NgFor, MatIcon, RouterOutlet, AsyncPipe],
18+
imports: [NgIf, NgFor, MatIcon, RouterOutlet],
2119
animations: [
2220
trigger('breadcrumb', [
2321
transition('* <=> *', [
@@ -50,36 +48,33 @@ import { NgIf, NgFor, AsyncPipe } from '@angular/common';
5048
])
5149
]
5250
})
53-
export class ChecklistOverviewComponent implements OnInit {
54-
breadcrumb$: Observable<any>;
51+
export class ChecklistOverviewComponent {
52+
private store = inject<Store<ApplicationState>>(Store);
53+
private router = inject(Router);
54+
private route = inject(ActivatedRoute);
55+
breadcrumbs = this.store.selectSignal(ChecklistSelectors.getBreadcrumb);
5556

56-
constructor(private store: Store<ApplicationState>, private router: Router, private route: ActivatedRoute) {}
57+
constructor() {
58+
const params = toSignal(this.route.params);
59+
const categories = this.store.selectSignal(ChecklistSelectors.getActiveCategories);
60+
const entities = this.store.selectSignal(ChecklistSelectors.getActiveCategoryEntities);
61+
const editMode = this.store.selectSignal(ChecklistSelectors.getEditMode);
5762

58-
ngOnInit() {
59-
this.breadcrumb$ = this.store.pipe(select(ChecklistSelectors.getBreadcrumb));
60-
61-
this.route.params
62-
.pipe(
63-
switchMap(_ =>
64-
zip(
65-
this.store.pipe(selectOnce(ChecklistSelectors.getActiveCategoryEntities)),
66-
this.store.pipe(selectOnce(ChecklistSelectors.getActiveCategories)),
67-
this.store.pipe(selectOnce(ChecklistSelectors.getEditMode))
68-
)
69-
),
70-
filter(([, categories]) => !!categories.length),
71-
tap(([entities, categories, editMode]) => {
63+
effect(() => {
64+
const _ = params();
65+
untracked(() => {
66+
if (categories().length) {
7267
const { category } = extractRouteParams(this.route.snapshot, 1);
73-
const categoryDisabled = !category || !entities[category];
68+
const categoryDisabled = !category || !entities()[category];
7469

75-
if (categoryDisabled && !editMode) {
76-
this.router.navigate([categories[0].slug], {
70+
if (categoryDisabled && !editMode()) {
71+
this.router.navigate([categories()[0].slug], {
7772
relativeTo: this.route
7873
});
7974
}
80-
})
81-
)
82-
.subscribe();
75+
}
76+
});
77+
});
8378
}
8479

8580
goBack(last: boolean) {
@@ -89,7 +84,7 @@ export class ChecklistOverviewComponent implements OnInit {
8984
}
9085
}
9186

92-
trackByTitle(_, item: Category | ChecklistItem) {
87+
trackByTitle(_, item: BreadcrumbItem) {
9388
return item.title;
9489
}
9590
}

src/app/checklist/checklist-search/checklist-search.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<input
22
placeholder="Search"
33
spellcheck="false"
4-
(focus)="focus$.next('FOCUS')"
4+
(focus)="focused()"
55
[formControl]="searchField"
66
[matAutocomplete]="auto"
77
/>
@@ -10,7 +10,7 @@
1010
[displayWith]="getOptionText.bind(this)"
1111
(optionSelected)="optionSelected($event)"
1212
>
13-
<mat-option *ngFor="let result of results$ | async" [value]="result">
13+
<mat-option *ngFor="let result of results()" [value]="result">
1414
<span *ngIf="result.document.category" class="result-type">{{ result.document.category }}</span>
1515
<span class="text" [innerHTML]="result.text"></span>
1616
</mat-option>

0 commit comments

Comments
 (0)