Angular & Rx JS: как управлять ошибками от служб к компоненту - PullRequest
0 голосов
/ 25 апреля 2020

У меня есть ApiService, который предназначен для отправки запросов на сервер отдыха. Он используется несколькими ComponentServices для восстановления и отправки данных с / на сервер

Например: UsernewComponent & UserlistComponent используют userService, который использует apiService.

Я хотел бы передавать ошибки от служб к конечным компонентам чтобы иметь возможность показывать модал или что-то еще при возникновении ошибки.

Но мне это не удается ...

Спасибо за вашу помощь! NB: поскольку я новичок в Angular & Rx js, если вы видите что-то в моем коде, что может быть лучше, пожалуйста, сообщите мне; -)

api.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse  } from '@angular/common/http';
import { throwError } from 'rxjs';
import { map, catchError, timeout } from 'rxjs/operators';


@Injectable({
  providedIn: 'root'
})
export class ApiService {

  private baseEndpoint = 'http://myurl/projserver/api/';
  private maxTime = 5000;

  constructor(private httpClient: HttpClient) { }

  public getEntriesFromRest (option: string): any {
    return this.httpClient.get<any[]>(this.baseEndpoint + option)
        .pipe(
            timeout(this.maxTime),
            catchError(this.handleError),
            map((data) => data['hydra:member'])
        );
  }

  public getEntryFromRest (option: string): any {
    return this.httpClient.get<any>(this.baseEndpoint + option)
        .pipe(
            timeout(this.maxTime),
            catchError(this.handleError)
        );
  }

  handleError(error: HttpErrorResponse) {
    let errorMessage = 'Unknown error!';
    if (error.error instanceof ErrorEvent) {
      // Client-side errors
      errorMessage = `Error: ${error.error.message}`;
    } else {
      // Server-side errors
      errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
    }
    return throwError(errorMessage);
  }
}

user.service.ts

import { User } from '../models/User.model';
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { ApiService } from './api.service';
import { throwError } from 'rxjs';

@Injectable({
  providedIn: 'root'
})

export class UserService {

  private users: User[] = [];
  private usersSubject = new Subject<User[]>();
  usersSubject$ = this.usersSubject.asObservable(); 

  private user: User;
  private userSubject = new Subject<User>();
  userSubject$ = this.userSubject.asObservable();

  constructor(private apiService: ApiService) { }

  emitUsers() {
    this.usersSubject.next(this.users.slice());
  }

  emitUser() {
    this.userSubject.next(this.user);
  }

  addUser(user: User) {
    this.users.push(user);
    this.emitUsers();
  }

  editUser(user: User) {
    var index = this.users.findIndex((item) => item.id === user.id);
    this.users.splice(index, 1, user);
    this.emitUsers();
  }

  getUsersFromRest() {
    this.users=[];
    this.apiService.getEntriesFromRest('users').subscribe(
    (data: any[]) => { data.forEach( (item: User) => {
            this.users.push(item);});
            this.emitUsers();
        },
    (error) => {
            console.log('UserService: '+error);
            this.userSubject.next(error);
            return throwError(error);
        });
  }

  getUserFromRest(userId: number) {
    this.apiService.getEntryFromRest('users/'+userId).subscribe(
        (data: User) => {
            this.user=data;
            this.emitUser();
        },
        (error) => {
            console.log('UserService: '+error);
            this.userSubject.next(error);
            return throwError(error);
        }
    );
  }

}

usernew.component.ts

import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormBuilder, FormGroup, Validators, FormArray, FormControl } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
import { User } from '../../../models/User.model';
import { Role } from '../../../models/Role.model';
import { UserService } from '../../../services/user.service';
import { MustMatch } from '../../../validators/must-match.validator';
import { NbDialogService } from '@nebular/theme';
import { DialogComponent } from '../../../components/dialog/dialog.component';

@Component({
  selector: 'usernew',
  templateUrl: './usernew.component.html',
  styleUrls: ['./usernew.component.scss']
})
export class UsernewComponent implements OnInit, OnDestroy {

  userIdSubscription: Subscription;
  userSubscription: Subscription;
  user: User;

...

  constructor(private formBuilder: FormBuilder,
              private userService: UserService,
              private router: Router,
              private route: ActivatedRoute,
              private nbDialogService: NbDialogService
              ) { }

  ngOnInit() {
    this.initForm();
    this.userSubscription = this.userService.userSubject$.subscribe(
      (user: User) => {
        if (user) {
            this.user = user;           
            this.editUser(this.user);
        }
      },
      (error) => {
            console.log('Component: '+error);   
        }
    );
    this.userIdSubscription = this.route.paramMap.subscribe(params => {
        const userId = +params.get('id');           //cast to number
        if(userId) {
            this.view='Edit';
            this.loading = true;
            this.userService.getUserFromRest(userId);
        }
    });
  }

  ngOnDestroy() {
    this.userIdSubscription.unsubscribe();
    this.userSubscription.unsubscribe();
  }

...
}

userlist.component.ts

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';
import { Router, ActivatedRoute  } from '@angular/router';
import { User } from '../../../models/User.model';
import { UserService } from '../../../services/user.service';

@Component({
  selector: 'userlist',
  templateUrl: './userlist.component.html',
  styleUrls: ['./userlist.component.scss']
})
export class UserlistComponent implements OnInit, OnDestroy {

...
  users: User[] = [];
  usersSubscription: Subscription;
  loading = false;

  constructor(
    private userService: UserService,
    private router: Router,
    private route: ActivatedRoute,
    ) { }

  ngOnInit() {
    this.usersSubscription = this.userService.usersSubject$.subscribe(
      (users: User[]) => {
        this.users = users;
        this.loading = false;
      },
      (error) => {
        console.log("Userlist Component: "+error);
    }
    );
    this.loading = true;
    this.userService.getUsersFromRest();
    //this.userService.emitUsers();
  }

  ngOnDestroy() {
    this.usersSubscription.unsubscribe();
  }

...

}

1 Ответ

0 голосов
/ 25 апреля 2020

Вы реализовали ApiService, чтобы обеспечить некоторое общее поведение для всех ваших вызовов http: - тайм-аут - обработка ошибок

Это нормально, но вы все равно должны помнить, чтобы использовать эту службу в каждом из ваших других Сервисы. Кроме того, вам все еще приходится обрабатывать ошибки в ваших «реальных» службах.

Я советую вам использовать HttpInterceptor для реализации этого вида обычного поведения. Вы можете найти некоторые ценные сведения здесь: https://medium.com/angular-in-depth/top-10-ways-to-use-interceptors-in-angular-db450f8a62d6

...