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

时间:2018-05-02 18:49:54

标签: angular typescript http angular-http

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

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

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

TestComponent

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内,然后让我的所有组件都使用该对象。

4 个答案:

答案 0 :(得分:3)

您应该使用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返回最后一个值,而常规observable仅在收到onnext时触发。

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

答案 1 :(得分:1)

我做一个标准做法来处理来自任何类型的pub / sub

的nulls / undefined

MySharedService

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

}

TestComponent

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模式从服务获取所需的实际数据时获取更新。我发现它更容易使用,并且可以用于较小的应用程序或插入指令和组件。

我认为在你的情况类型中的关键是处理有数据的地方以及没有数据的地方。这方面的好例子是&#34; ... loading&#34; Angular预先填充您使用Angular CLI创建的每个组件的视图的文本。

请记住,如果您打算使用obOables来使用ngOnDestroy清理它们(或者可能会发生意外行为)

祝你好运!!

答案 2 :(得分:0)

TestComponent 内部调用服务jus订阅时,也请在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
      });
  }

}

答案 3 :(得分:0)

试试并告诉我。

MySharedService

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

}

TestComponent

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