Angular5 HttpClient,在 GET api 调用后设置变量

Angular5 HttpClient, set variables after GET api call

我有多个组件使用的服务(我们称之为 MySharedService)。在 MySharedService 内,我调用了另一个服务,该服务进行 API 调用。 MySharedService 持有在我的 GET 调用后分配的 JavaScript 对象。

我的问题是我的组件依赖 JavaScript 对象在它们的构造函数中设置它们的值。当 JavaScript 由于 API 调用尚未完成而可能未定义时,我如何设置它们的值?这是一个示例:

ApiService

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { HttpClient } from '@angular/common/http';
/* Other imports */

@Injectable()
export class ApiService {

    constructor(private http: HttpClient) { }

    getTestData(): Observable<MyDataInterface> {
        const API_URL = 'some_url_here';
        return this.http.get<MyDataInterface>(API_URL, { observe: 'response' });
    }
}

MySharedService

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
/* Other imports */

@Injectable()
export class MySharedService {

    myData: MyDataInterface;

    constructor(private apiServie: ApiService) {
        this.apiService.getTestData().subscribe((response) => {
            this.myData = { ...response.body };
            console.log(this.myData);
        });
    }
}

测试组件

import { Component, OnInit } from '@angular/core';
import { MySharedService } from '../../../services/myshared.service';
/* Other imports */    

@Component({
    selector: 'app-test',
    templateUrl: './test.component.html',
    styleUrls: ['./test.component.css']
})
export class TestComponent implements OnInit {

    name: string;
    /* Other variables here. */

    constructor(private mySharedService: MySharedService) {
        // Error here because myData has not been populated, yet.
        this.name = this.mySharedService.myData.name;
    }
}

所以当我尝试从 myData 对象访问数据时,问题发生在我的组件内部,因为它还没有被填充(console.log() 确实在几秒钟)。我应该如何获取这些值?我只想调用一次其余服务并将对象保存在 MySharedService 中,然后让我的所有组件都使用该对象。

我将处理来自任何类型的 pub/sub

的 nulls/undefined 作为标准做法

我的共享服务

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
/* Other imports */

@Injectable()
export class MySharedService {

public message = new Subject<any>();

myData: MyDataInterface;

constructor(private apiServie: ApiService) {
    this.apiService.getTestData().subscribe((response) => {
        if(response){
           this.myData = { ...response.body };
           this.sendUpdate(this.myData);
           console.log(this.myData);
        }else console.log("I guess you could do some sort of handling in the else")
    });


    sendUpdate(message) {
        this.message.next(message);
    }

    getUpdate(): Observable<any> {
    return this.message.asObservable();
    }

    clearUpdate() {
        this.message.next();
    }

}

测试组件

import { Component, OnInit } from '@angular/core';
import { MySharedService } from '../../../services/myshared.service';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/takeWhile';
/* Other imports */    

@Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.css']
})
export class TestComponent implements OnInit, OnDestroy {

subscription: Subscription;
private alive: boolean = true;
name: string;
/* Other variables here. */

constructor(private mySharedService: MySharedService) {
    // Error here because myData has not been populated, yet.
    this.subscription = this.mySharedService.getUpdate().takeWhile(() => this.alive).subscribe(message => {
            if (message) {
               this.name = message;
            }
        });
    this.name = this.mySharedService.myData.name;
   }
}
    //Best practice to clean up observables
    ngOnDestroy() {
    this.updateService.clearUpdate();
    this.subscription.unsubscribe();
    this.alive = false;
}

我所做的编辑充分利用了使用 Rxjs 订阅功能 IMO 的现代 Angular 服务。在过去的一年里,我一直在

之间分配时间

A) 使用带有 Flux/Redux/Mobx 的存储在应用程序周围传递已解析的数据。等待用observable解析数据或者使用Select方法获取数据

B) 使用 pub/sub 模式在服务具有您需要的实际数据时从服务获取更新。我发现它更易于使用,对于较小的应用程序或在各处插入指令和组件也很方便。

我认为在您这种情况下,关键是处理有数据的地方以及没有数据的地方。这方面的一个很好的例子是 Angular 预填充您使用 Angular CLI 创建的每个组件的视图的“...加载”文本。

请记住,如果您要使用可观察对象来使用 ngOnDestroy 清理它们(否则可能会发生意外行为)

祝你好运!!

在您的 TestComponent 中调用服务时只需订阅如下,也可以在 ngOnInit 事件中调用它

ngOnInit() {

  if (this.mySharedService.myData) {
    this.name = this.mySharedService.myData.name;
  } else {
    this.apiService.getTestData().subscribe(
      data => {
        // On Success
        this.mySharedService.myData = data.body.myData;
        this.name = this.mySharedService.myData.name;
      },
      err => {
        // On Error
      });
  }

}

试试这个然后告诉我。

我的共享服务

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
/* Other imports */

@Injectable()
export class MySharedService {

    myData: MyDataInterface;

    constructor(public apiService: ApiService) {
    }

    fetchData() {
       return this.apiService.getTestData();
    }

}

测试组件

import { Component, OnInit } from '@angular/core';
import { MySharedService } from '../../../services/myshared.service';
/* Other imports */    

@Component({
    selector: 'app-test',
    templateUrl: './test.component.html',
    styleUrls: ['./test.component.css']
})
export class TestComponent implements OnInit {

    name: string;
    /* Other variables here. */

    constructor(private mySharedService: MySharedService) {
        this.mySharedService.fetchData().subscribe((resp: any) => {
        this.name = resp.body; 
     });
    }
}

你应该使用 BehaviorSubject

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
/* Other imports */

@Injectable()
export class MySharedService {

    myData: BehaviorSubject<MyDataInterface> = new BehaviorSubject(null);

    constructor(private apiServie: ApiService) {
        this.apiService.getTestData().subscribe((response) => {
            this.myData.next({ ...response.body });
            console.log(this.myData.getValue());
        });
    }
}

在组件构造函数中进行订阅是一种不好的做法,请改用 ngOnInit 生命周期挂钩。

export class TestComponent implements OnInit {

    name: string;
    /* Other variables here. */

    constructor(private mySharedService: MySharedService) {
    }

    ngOnInit() {
        this.mySharedService.myData.subscribe((data: MyDataInterface) => { 
            if (data)
                this.name = data.name;
        });
    }
}

网络调用只会进行一次,数据会缓存在 BehaviorSubject 中,所有组件都会订阅。

现在为什么要使用 BehaviorSubject 而不是 Observable?因为,

  • 订阅后 BehaviorSubject returns 最后一个值 而常规可观察对象仅在收到 onnext 时触发。

  • 如果你想在不可观察代码(没有订阅)中检索BehaviorSubject的最后一个值,你可以使用getValue()方法。