Search code examples
angularsnackbarangular17angular-standalone-components

How to use a SnackBarService in a HttpInterceptorFn using standalone components?


I'm trying to display the response I get from my backend. So, I created a service to show snackbars, then I'd like to implement it in my interceptor. I'm using stand alone components, so I need to keep using functional component because of the provideHttpClient(withInterceptors...))

Here is my interceptor

import { SnackbarService } from '../services/snackbar.service';
import { inject } from '@angular/core';
import { HttpInterceptorFn } from '@angular/common/http';
import { catchError } from 'rxjs/operators';

export const errorHandlerInterceptor: HttpInterceptorFn = (req, next) => {
  const snackbarService: SnackbarService = inject(SnackbarService);

  return next(req).pipe(
    catchError((error) => {
      console.log(snackbarService);
      snackbarService.openErrorSnackbar('Error found: ' + error.error);
      return [];
    })
  );
};

here is my service

import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';

@Injectable({
  providedIn: 'root',
})
export class SnackbarService {
  constructor(private snackBar: MatSnackBar) {}

  public openErrorSnackbar(message: string): void {
    this.snackBar.open(message, 'Dismiss', {
      duration: 5000,
      panelClass: ['error-snackbar'],
    });
  }
}

my app.config

import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { routes } from './app.routes';
import { errorHandlerInterceptor } from './core/interceptors/error.interceptor';
import { provideAnimations } from '@angular/platform-browser/animations';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideAnimations(),
    provideHttpClient(withInterceptors([errorHandlerInterceptor])),
  ]
};

and my app.module

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { HttpClientModule } from '@angular/common/http'


@NgModule({
  declarations: [],
  imports: [
    BrowserModule,
    HttpClientModule,
    RouterModule.forRoot([]),
  ],
  bootstrap: [],
  providers: [HttpClientModule, ]
})
export class AppModule {}

So I tried to console log my interceptor, this works. It's the snackbar stuff that does not. I've tried

  1. change my functional service by a class
  2. add providers in the .module or .config
  3. display in one of my standalone components (not using the service to check if I can at least display a snackbar)

Solution

  • Here is a working example to demonstrate it works, I found few issues.

    1. When using standalone components, we do not BrowserModule, HttpClientModule, RouterModule.forRoot([]) we can remove them.

    We can configure it like below:

    import '@angular/localize/init';
    import { importProvidersFrom } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import { provideHttpClient, withInterceptors } from '@angular/common/http';
    import { provideAnimations } from '@angular/platform-browser/animations';
    import { VERSION as CDK_VERSION } from '@angular/cdk';
    import {
      VERSION as MAT_VERSION,
      MatNativeDateModule,
    } from '@angular/material/core';
    import { SnackBarOverviewExample } from './example/snack-bar-overview-example';
    import { errorHandlerInterceptor } from './app/test.interceptor';
    import { provideRouter } from '@angular/router';
    
    /* eslint-disable no-console */
    console.info('Angular CDK version', CDK_VERSION.full);
    console.info('Angular Material version', MAT_VERSION.full);
    
    bootstrapApplication(SnackBarOverviewExample, {
      providers: [
        provideAnimations(),
        provideHttpClient(),
        provideRouter([]),
        importProvidersFrom(MatNativeDateModule),
        provideHttpClient(withInterceptors([errorHandlerInterceptor])),
      ],
    }).catch((err) => console.error(err));
    

    Please focus on the providers array above, the rest might be irrelevant.

    stackblitz