In order to display user friendly error messages, I've created a service that hosts all error messages as templates, which can be requested from any component and potentially enriched with certain labels.
export class ErrorService {
private _errors: { [key: string]: (text?: string) => string } = {
'400.1': () => 'Datensatz darf nicht leer sein.',
'400.2': () => 'Datensatz mit diesem Titel bereits vorhanden.',
'400.3': (fieldname?: string) => `${fieldname} darf nicht leer sein.`,
'400.4': (filetype?: string) => `Datei muss vom Typ ${filetype} sein.`,
'400.5': () => 'Datei muss eine Pixelgrafik sein.',
'404': () => 'Seite nicht gefunden!',
'404.1': () => 'Eintrag nicht gefunden!',
'500': () => 'Interner Serverfehler.',
'500.0': () => 'Verbindung zur Datenbank konnte nicht aufgebaut werden.',
'500.1': () => 'Eintrag konnte nicht geschrieben werden.'
}
public getError(errorCode: string): (text?: string) => string {
return this._errors[errorCode] || (() => 'Unbekannter Fehler.');
}
}
So, for example, if there is a required "title" input field in a form, the component would then bind to:
<mat-error *ngIf="titleControl.errors?.['required']">
{{errorService.getError('400.3')('Titel')}}
</mat-error>
Which would then display the message: "Titel darf nicht leer sein." ("Title may not be empty.")
Some of these messages need to be displayed as a result of a database call, which is made from a DbFacadeService
, like this:
public read(dataType: DataType, id: string): Observable<DataEntry | undefined> {
return this._http.get<DataEntry>(`${this.baseURL}/${dataType}/${id}`,
{ observe: 'response' })
.pipe(map((response: HttpResponse<DataEntry>) => response.body || undefined),
first());
}
So the component could bind to this and reroute to an error page, which then displays the user friendly message based on the error code as url parameter:
this._dbFacadeService.read(this._dataType, entryId)
.subscribe((entry: DataEntry) => ...)
.error((code: string) => this._router.navigate(['/error', code]));
The database does something like this (it's not implemented in this specific function yet, so there's no error check for what the actual error is, so for now let's just assume the "entry wasn't found").
Area.findById(request.params.id)
.select('-__v')
.exec((error, area) => error ? result.status(404).send({ code: '404.1',
message: error.message)
: result.status(200).json(area));
Of course there might be error messages I have no control over, like the database being unreachable, which will simply return a regular HttpErrorResponse
of which I need to use its code. I have to map both and respond to them with the proper redirect and user friendly error message.
So, in the read message of my DbFacadeService
I would need something like this:
.pipe(map((response: HttpResponse<DataEntry>) => response.body || undefined),
mapError((error: HttpErrorResponse) => error.data?.code || `${error.status}`)
first());
Is there anything alike? Or how may I best achieve this without having the component calling this._dbFacadeService.read(...)
to deal with the conversion in its error call?
No idea how this didn't show up earlier when I was looking for a solution but I eventually found it.
.pipe(map((response: HttpResponse<DataEntry>) => response.body || undefined)
first(),
catchError((response: HttpErrorResponse) => {
throw response.error?.code || `${response.status}`);
});