Angular 2:如何处理组件中服务的异步错误?

时间:2016-06-23 15:00:36

标签: javascript angular

假设我们有一个服务,它会对API进行http调用以创建用户。根据结果​​(200或错误),应用程序应重定向或显示错误(客户端有一些验证,但这不是主题,因为验证应始终在服务器端进行)。

Angular 2文档声明,让服务返回一个observable并在组件中订阅它是不好的做法。该服务应该是自包含的,组件不应该对此有任何了解。拨打userService.createUser(user_data);

就足够了

但是再次路由应该在组件中发生。所以我想知道如何处理这种情况?我的服务应该返回一个新的Observable吗?或者只是一个价值?但那我该如何处理异步任务呢?

这样做的正确方法是什么?

让我们以此服务创建用户和此组件为例:

// user.service.ts:

import { Http, Headers } from '@angular/http';
import { Inject, Injectable } from '@angular/core';

import { API_CONFIG, Config } from '../config/api.config';
import { User } from '../models/user.model';

@Injectable()
export class UserService {
  validation_errors: Array<any> = [];

  constructor(
    @Inject(API_CONFIG) private api_config: Config,
    private http: Http,
    @Inject(User) public user: User
  ) {}

  createUser(user: User) {
    var body = JSON.stringify({ user });
    var myHeader = new Headers();
    myHeader.append('Content-Type', 'application/json');

    this.http.post(this.api_config.apiEndpoint + '/users/', body, { headers: myHeader })
      .map(res => res.json())
      .subscribe(
        res => {
          // User was created successfully
          this.user = this.fromJson(res.data);
        },
        err => {
          // Something went wrong, let's collect all errors in a class attribute
          let errors = err.json().errors;
          for(var i = 0; i < errors.length; i++) {
            this.validation_errors.push(errors[i])
          }
        }
      );
  }

  /**
  * @param input_json JSON returned from API, formatted according to jsonapi.org, containing one single user.
  * @return UserModel instantiated with the values from input_json
  */
  fromJson(input_json: any) {
    var user:User = new User();
    user = input_json.attributes;
    user.id = input_json.id;
    return user;
  }
}

// user.component.ts:

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, REACTIVE_FORM_DIRECTIVES, Validators } from '@angular/forms';
import { Router, RouteParams, ROUTER_DIRECTIVES } from '@angular/router-deprecated';

import { UserService } from '../../shared/index';

@Component({
  selector: 'fac-user-editor',
  templateUrl: 'app/+users/editor/user-editor.component.html',
  styleUrls: ['app/+users/editor/user-editor.component.css'],
  directives: [REACTIVE_FORM_DIRECTIVES, ROUTER_DIRECTIVES],
  providers: [UserService]
})
export class UserEditorComponent implements OnInit {

  // Setup Form
  private email_regex = '[a-z0-9\\.\\-\\_]+@[a-z0-9\\.\\-\\_]+\\.[a-z0-9\\.\\-\\_]+';
  userForm: FormGroup;
  action: string;
  idCtrl = new FormControl('');
  nameCtrl = new FormControl('', [Validators.required]);
  emailCtrl = new FormControl('', [Validators.required, Validators.pattern(this.email_regex)]);
  usernameCtrl = new FormControl('', [Validators.required, Validators.minLength(5)]);
  passwordCtrl = new FormControl('', [Validators.minLength(8)]);
  passwordConfirmationCtrl = new FormControl('');

  public user_id: string;


  constructor(private userService: UserService, private router: Router, private params: RouteParams) {}

  /**
  * Handle submit of form
  */
  onSubmit(form: any) {
    // Here should happen some error handling / routing, depending on the result of the call to the API
    this.userService.createUser(this.userService.user);
  }

  ngOnInit(): any {

    this.userForm = new FormGroup({
      id: this.idCtrl,
      name: this.nameCtrl,
      email: this.emailCtrl,
      username: this.usernameCtrl,
      password: this.passwordCtrl,
      password_confirmation: this.passwordConfirmationCtrl
    });

  }

}

我可以在模板中显示错误消息,如下所示:

<div class="form-group"
  [hidden]="adminService.validation_errors.length === 0"
  class="alert alert-warning" role="alert">
  <strong>Some errors occured</strong>
  <ul>
    <li *ngFor="let validation_error of adminService.validation_errors">
      <span class="text-capitalize">{{validation_error.source.field}}:</span> {{validation_error.detail}}
    </li>
  </ul>
</div>

1 个答案:

答案 0 :(得分:1)

这就是我解决问题的方法。我的服务现在使用.map()来转换从后端返回的数据(JSON)并返回一个Observable,它将初始化我的用户模型。 Component可以订阅observable。数据存储在组件的变量中,以及验证错误。

我还使用FormBuilder来构建新的Form API。这应该适用于RC 3。

我希望这有助于某人。

// user.service.ts:

import { Http, Headers } from '@angular/http';
import { Inject, Injectable } from '@angular/core';

import { API_CONFIG, Config } from '../config/api.config';
import { User } from '../models/user.model';

@Injectable()
export class UserService {

  constructor(
    @Inject(API_CONFIG) private api_config: Config,
    private http: Http,
    @Inject(User) public user: User
  ) {}

  createUser(user: User) {
    var body = JSON.stringify({ user });
    var myHeader = new Headers();
    myHeader.append('Content-Type', 'application/json');

    // Use map to transform reply from server (JSON to User Object) and return an Observable 
    // The component can subscribe to.
    return this.http.post(this.api_config.apiEndpoint + '/users/', body, { headers: myHeader })
      .map(res => res.json())
      .map(res => {
        return this.fromJson(res.data);
      });

  }

  /**
  * @param input_json JSON returned from API, formatted according to jsonapi.org, containing one single user.
  * @return UserModel instantiated with the values from input_json
  */
  fromJson(input_json: any) {
    var user:User = new User();
    user = input_json.attributes;
    user.id = input_json.id;
    return user;
  }
}

// user.component.ts:

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, FormBuilder, REACTIVE_FORM_DIRECTIVES, Validators } from '@angular/forms';
import { Router, RouteParams, ROUTER_DIRECTIVES } from '@angular/router-deprecated';
// Include the User Model
import { User, UserService } from '../../shared/index';

@Component({
  selector: 'fac-user-editor',
  templateUrl: 'app/+users/editor/user-editor.component.html',
  styleUrls: ['app/+users/editor/user-editor.component.css'],
  directives: [REACTIVE_FORM_DIRECTIVES, ROUTER_DIRECTIVES],
  providers: [User, UserService]
})
export class UserEditorComponent implements OnInit {

  // Setup Form
  private email_regex = '[a-z0-9\\.\\-\\_]+@[a-z0-9\\.\\-\\_]+\\.[a-z0-9\\.\\-\\_]+';
  userForm: FormGroup;
  validation_errors: Array<string> = [];
  user_id: string;

  constructor(private userService: UserService, private user: User, private router: Router, private params: RouteParams, private formBuilder: FormBuilder) {}

  /**
  * Handle submit of form
  */
  onSubmit(form: any) {
    // Subscribe to observable and handle errors
    this.userService.createUser(this.userService.user).subscribe(
      user => {
        // Store created user in Component variable. We could now display it or navigate somewhere
        this.user = user;
      },
      err => {
        let errors = err.json().errors;
        for(var i = 0; i < errors.length; i++) {
          // Handle errors in the component and don't store it in the Service
          this.validation_errors.push(errors[i])
        }
      }
    );
  }

  ngOnInit(): any {
    // Use the new FormBuilder
    this.userForm = this.formBuilder.group({
      id: [''],
      name: ['', Validators.required],
      email: ['', [Validators.required, Validators.pattern(this.email_regex)]],
      username: ['', [Validators.required, Validators.minLength(5)]],
      password: ['', [Validators.required, Validators.minLength(8)]],
      password_confirmation: ['']
    });

  }

}

user-editor.component.html

<div class="form-group"
  [hidden]="validation_errors.length === 0"
  class="alert alert-warning" role="alert">
  <strong>Some errors occured</strong>
  <ul>
    <li *ngFor="let validation_error ofvalidation_errors">
      <span class="text-capitalize">{{validation_error.source.field}}:</span> {{validation_error.detail}}
    </li>
  </ul>
</div>