Skip to content

Commit 75730d5

Browse files
committed
feat: show error notification when HTTP error response received
1 parent 547a74a commit 75730d5

File tree

6 files changed

+115
-2
lines changed

6 files changed

+115
-2
lines changed

src/app/app.module.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { NgModule } from '@angular/core';
1+
import { NgModule, Provider } from '@angular/core';
22
import { BrowserModule } from '@angular/platform-browser';
33

44
import { AppRoutingModule } from './app-routing.module';
@@ -11,11 +11,21 @@ import { MatIconModule } from '@angular/material/icon';
1111
import { MatMenuModule } from '@angular/material/menu';
1212
import { MatTooltipModule } from '@angular/material/tooltip';
1313
import { ProductsModule } from './products/products.module';
14-
import { HttpClientModule } from '@angular/common/http';
14+
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
1515
import { MatBadgeModule } from '@angular/material/badge';
1616
import { CartModule } from './cart/cart.module';
1717
import { CONFIG_TOKEN } from './core/injection-tokens/config.token';
1818
import { environment } from '../environments/environment';
19+
import { ErrorPrintInterceptor } from './core/interceptors/error-print.interceptor';
20+
import { MatSnackBarModule } from '@angular/material/snack-bar';
21+
22+
const interceptors: Provider[] = [
23+
{
24+
provide: HTTP_INTERCEPTORS,
25+
useClass: ErrorPrintInterceptor,
26+
multi: true,
27+
},
28+
];
1929

2030
@NgModule({
2131
declarations: [AppComponent, HeaderComponent],
@@ -32,8 +42,10 @@ import { environment } from '../environments/environment';
3242
CartModule,
3343
HttpClientModule,
3444
MatBadgeModule,
45+
MatSnackBarModule,
3546
],
3647
providers: [
48+
interceptors,
3749
{
3850
provide: CONFIG_TOKEN,
3951
useValue: environment,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { TestBed } from '@angular/core/testing';
2+
3+
import { ErrorPrintInterceptor } from './error-print.interceptor';
4+
5+
describe('ErrorPrintInterceptor', () => {
6+
beforeEach(() =>
7+
TestBed.configureTestingModule({
8+
providers: [ErrorPrintInterceptor],
9+
})
10+
);
11+
12+
it('should be created', () => {
13+
const interceptor: ErrorPrintInterceptor = TestBed.inject(
14+
ErrorPrintInterceptor
15+
);
16+
17+
expect(interceptor).toBeTruthy();
18+
});
19+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Injectable } from '@angular/core';
2+
import {
3+
HttpEvent,
4+
HttpHandler,
5+
HttpInterceptor,
6+
HttpRequest,
7+
} from '@angular/common/http';
8+
import { Observable } from 'rxjs';
9+
import { NotificationService } from '../notification.service';
10+
import { tap } from 'rxjs/operators';
11+
12+
@Injectable()
13+
export class ErrorPrintInterceptor implements HttpInterceptor {
14+
constructor(private readonly notificationService: NotificationService) {}
15+
16+
intercept(
17+
request: HttpRequest<unknown>,
18+
next: HttpHandler
19+
): Observable<HttpEvent<unknown>> {
20+
return next.handle(request).pipe(
21+
tap({
22+
error: () => {
23+
const url = new URL(request.url);
24+
25+
this.notificationService.showError(
26+
`Request to "${url.pathname}" failed. Check the console for the details`,
27+
0
28+
);
29+
},
30+
})
31+
);
32+
}
33+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { TestBed } from '@angular/core/testing';
2+
3+
import { NotificationService } from './notification.service';
4+
5+
describe('NotificationService', () => {
6+
let service: NotificationService;
7+
8+
beforeEach(() => {
9+
TestBed.configureTestingModule({});
10+
service = TestBed.inject(NotificationService);
11+
});
12+
13+
it('should be created', () => {
14+
expect(service).toBeTruthy();
15+
});
16+
});

src/app/core/notification.service.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Injectable } from '@angular/core';
2+
import { MatSnackBar } from '@angular/material/snack-bar';
3+
4+
@Injectable({
5+
providedIn: 'root',
6+
})
7+
export class NotificationService {
8+
constructor(private readonly snackBar: MatSnackBar) {}
9+
10+
/**
11+
* Show message with an error
12+
*
13+
* @param text Error text message
14+
* @param duration Duration to close after. 0 to close manually only
15+
*/
16+
showError(text: string, duration = 3000) {
17+
this.snackBar.open(text, 'Dismiss', {
18+
duration,
19+
panelClass: 'shop-snackbar-error',
20+
});
21+
}
22+
}

src/styles.scss

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,14 @@ body {
2020
// Fix vertical alignment for Firefox
2121
line-height: 1 !important;
2222
}
23+
24+
// snackbar styles
25+
.shop-snackbar-error {
26+
background-color: #ff0015;
27+
color: white;
28+
29+
button {
30+
color: white;
31+
border: 1px solid;
32+
}
33+
}

0 commit comments

Comments
 (0)