处理嵌套的可观察对象

Handling nested observables

我在 Angular 2 开始一个项目,这是我第一次处理 Observables,对一个非常具体的案例有一些疑问。

我有一个 API 服务是这样的:

@Injectable()
export class UserAPI {

  [...]

  login(email: string, password: string): Observable<Response> {
    let params = new URLSearchParams();
    params.set('email', email);
    params.set('password', password);

    return this.$http.request(
      new Request({
        method: RequestMethod.Post,
        url: this.baseUrl + '/login',
        search: params
      }))
      .map((response: Response) => response.json());
  }

  [...]
}

我在用户服务上管理用户会话及其配置文件数据(当其会话处于活动状态时),该服务包含调用此 API:

的方法
@Injectable()
export class UserService {

  [...]

  constructor(private userAPI: UserAPI, private storage: StorageService) {
  }

  login(email: string, password: string) {
    return this.userAPI.login(email, password).subscribe(
      (res) => {
        this.storage.store(Constants.storage.session, res.token);
      },
      (err) => {
        this.storage.destroy(Constants.storage.session);
      }
    );
  }

  [...]
}

现在,我还有一个处理登录表单的组件,如果成功,会将用户重定向到应用程序的私有区域,但如果失败,则会显示错误消息 (401 , 或者任何其他偶然发生的错误):

@Component({
  moduleId: module.id,
  selector: "auth-login",
  templateUrl: "auth.login.html"
})
export class LoginComponent {

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

  [...]

  login(): void {
    this.form.loading = true;
    this.form.error = -100; // arbitrary number

    this.userService.login(this.form.email, this.form.password);
      /*
      .subscribe(
      (res) => {
        this.router.navigate(["/home", {}]);
      },
      (err) => {
        this.form.error = err.status;
      },
      () => {
        this.form.loading = false;
      }
    );
    */
  }

  [...]
}

请注意 userService.login() 函数调用中的注释代码。这些是我打算对调用的 success/error 执行的说明,但我无法让它工作。我想把事情分开:用户服务管理会话部分,组件处理重定向或向用户显示出了什么问题。

我做错了什么?到这里怎么走?嵌套的可观察对象?在组件和用户服务之间使用承诺?其他一些方法?我一直在搜索这里,但找不到与我的相似的案例。

我不认为你可以像那样链接订阅;您实际上期望 thing.subscribe(...).subscribe(...) 工作。相反,您可以将 .map and .catch 用于服务内的操作,并让组件 .subscribe:

login(email: string, password: string) {
  return this.userAPI.login(email, password)
      .map(res => {
        this.storage.store(Constants.storage.session, res.token);
        return res;
      })
      .catch(err => {
        this.storage.destroy(Constants.storage.session);
        return Observable.throw(err);
      });
}