首页 > 解决方案 > Angular & RxJS:如何管理从服务到组件的错误

问题描述

我有一个旨在向 Rest 服务器发送请求的 ApiService。它被多个 ComponentServices 用于从/向服务器检索和发送数据

例如:UsernewComponent 和 UserlistComponent 使用使用 apiService 的 userService。

我想将错误从服务传输到最终组件,以便在发生错误时能够显示模式或其他内容。

但我不会管理它...

谢谢您的帮助 !注意:因为我是 Angular 和 Rxjs 的新手,如果你在我的代码中看到可以更好的东西,请告诉我 ;-)

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);
  }
}

用户服务.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();
  }

...

}

标签: angularerror-handlingrxjs

解决方案


您实现ApiService了为所有 http 调用提供一些常见行为: - 超时 - 错误处理

没关系,但您仍然必须记住在您的每一项其他服务中使用此服务。另外,您仍然必须处理“真实”服务中的错误。

我建议您使用 toHttpInterceptor来实现这种常见行为。您可以在这里找到一些有价值的信息:https ://medium.com/angular-in-depth/top-10-ways-to-use-interceptors-in-angular-db450f8a62d6


推荐阅读