Skip to content

Commit b6b17fc

Browse files
committed
error dialog
1 parent 4d2c26b commit b6b17fc

10 files changed

+146
-7
lines changed

src/app/app.component.html

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
@if (error()) {
2+
<app-error-modal title="An Error has occurred!" [message]="error()" />
3+
}
4+
15
<header>
26
<img src="logo.png" alt="Stylized globe" />
37
<h1>PlacePicker</h1>
4-
<p>
5-
Create your personal collection of places you would like to visit or you
6-
have visited.
7-
</p>
8+
<p>Create your personal collection of places you would like to visit or you have visited.</p>
89
</header>
910

1011
<main>

src/app/app.component.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1-
import { Component } from '@angular/core';
1+
import { Component, inject } from '@angular/core';
22

33
import { AvailablePlacesComponent } from './places/available-places/available-places.component';
44
import { UserPlacesComponent } from './places/user-places/user-places.component';
5+
import { ErrorService } from './services/error.service';
6+
import { ErrorModalComponent } from './shared/modal/error-modal/error-modal.component';
57

68
@Component({
79
selector: 'app-root',
810
standalone: true,
911
templateUrl: './app.component.html',
1012
styleUrl: './app.component.css',
11-
imports: [AvailablePlacesComponent, UserPlacesComponent],
13+
imports: [AvailablePlacesComponent, UserPlacesComponent, ErrorModalComponent],
1214
})
1315
export class AppComponent {
14-
styles = ['color: indigo', 'background: #90EE90', 'font-weight: bold', 'font-size: 18px'].join(';');
16+
private styles = ['color: indigo', 'background: #90EE90', 'font-weight: bold', 'font-size: 18px'].join(';');
17+
private errorServ = inject(ErrorService);
18+
protected error = this.errorServ.error;
1519

1620
constructor() {
1721
console.log(

src/app/services/error.service.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Injectable, signal } from '@angular/core';
2+
3+
@Injectable({
4+
providedIn: 'root',
5+
})
6+
export class ErrorService {
7+
private _error = signal('');
8+
9+
error = this._error.asReadonly();
10+
11+
showError(message: string) {
12+
console.error(message);
13+
this._error.set(message);
14+
}
15+
16+
clearError() {
17+
this._error.set('');
18+
}
19+
}

src/app/services/places.service.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { catchError, map, tap } from 'rxjs/operators';
66
import { throwError } from 'rxjs';
77

88
import { Place } from '../models/place.model';
9+
import { ErrorService } from './error.service';
910

1011
@Injectable({
1112
providedIn: 'root',
@@ -15,6 +16,7 @@ export class PlacesService {
1516
loadedUserPlaces = this.userPlaces.asReadonly();
1617

1718
private http = inject(HttpClient);
19+
private errorServ = inject(ErrorService);
1820

1921
loadAvailablePlaces() {
2022
return this.fetchPlaces('/api/v2/places', 'Error loading available places!');
@@ -47,6 +49,7 @@ export class PlacesService {
4749
.pipe(
4850
catchError(err => {
4951
this.userPlaces.set(prevPlaces);
52+
this.errorServ.showError('Unable to store the selected place!');
5053
return throwError(() => new Error('Unable to store the selected place!'));
5154
}),
5255
);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
.error {
2+
max-width: 40rem;
3+
margin: auto;
4+
padding: 1rem;
5+
background-color: #f4c7c7;
6+
color: #3e0505;
7+
}
8+
9+
.confirmation-actions {
10+
margin-top: 1rem;
11+
display: flex;
12+
justify-content: flex-end;
13+
gap: 1rem;
14+
}
15+
16+
button {
17+
cursor: pointer;
18+
font-family: 'Raleway', sans-serif;
19+
font-size: 1rem;
20+
padding: 0.5rem 1.5rem;
21+
border: none;
22+
border-radius: 4px;
23+
background-color: #5d0909;
24+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
25+
color: #fff;
26+
}
27+
28+
button:hover,
29+
button:focus {
30+
background-color: #3e0505;
31+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<app-modal>
2+
<div class="error">
3+
<h2>{{ title() }}</h2>
4+
<p>{{ message() }}</p>
5+
6+
<div class="confirmation-actions">
7+
<button class="button" (click)="onClearError()">Okay</button>
8+
</div>
9+
</div>
10+
</app-modal>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Component, inject, input } from '@angular/core';
2+
import { ModalComponent } from '../modal.component';
3+
import { ErrorService } from '../../../services/error.service';
4+
5+
@Component({
6+
selector: 'app-error-modal',
7+
standalone: true,
8+
templateUrl: './error-modal.component.html',
9+
styleUrl: './error-modal.component.css',
10+
imports: [ModalComponent],
11+
})
12+
export class ErrorModalComponent {
13+
title = input<string>();
14+
message = input<string>();
15+
16+
private errorServ = inject(ErrorService);
17+
18+
onClearError() {
19+
this.errorServ.clearError();
20+
}
21+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
dialog {
2+
min-width: 30rem;
3+
padding: 0;
4+
z-index: 2;
5+
background: #d5c7bc;
6+
border-radius: 8px;
7+
overflow: hidden;
8+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
9+
animation: slide-down-fade-in 0.3s ease-out forwards;
10+
}
11+
12+
dialog::backdrop {
13+
position: fixed;
14+
top: 0;
15+
left: 0;
16+
z-index: 1;
17+
width: 100%;
18+
height: 100vh;
19+
background: rgba(0, 0, 0, 0.6);
20+
}
21+
22+
@keyframes slide-down-fade-in {
23+
from {
24+
opacity: 0;
25+
transform: translateY(-3rem);
26+
}
27+
to {
28+
opacity: 1;
29+
transform: translateY(0);
30+
}
31+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<dialog #dialog>
2+
<ng-content />
3+
</dialog>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { AfterViewInit, Component, ElementRef, viewChild } from '@angular/core';
2+
3+
@Component({
4+
selector: 'app-modal',
5+
standalone: true,
6+
imports: [],
7+
templateUrl: './modal.component.html',
8+
styleUrl: './modal.component.css',
9+
})
10+
export class ModalComponent implements AfterViewInit {
11+
private dialogEl = viewChild.required<ElementRef<HTMLDialogElement>>('dialog');
12+
13+
ngAfterViewInit(): void {
14+
this.dialogEl().nativeElement.showModal();
15+
}
16+
}

0 commit comments

Comments
 (0)