将Observable值设置为可导出变量。 Angular v6

时间:2018-12-31 14:19:19

标签: angular typescript observable

我的服务

search (packageName: string, refresh = false): Observable<PackageInfo>
   {
    const options = createHttpOptions(packageName, refresh);
    this.searchResults = this.http.get(searchUrl + packageName, options) as Observable<PackageInfo>;
    return this.searchResults
  }

我能做

this.searchResults.subscribe(repoUrl => console.log(repoUrl.repos_url))

这将显示“我的观察”中的URL。我需要保存repos_url,以便可以进行第二个http.get调用。我不确定该怎么做。我的想法是将订阅保存到哪里,但是在console.log中返回未定义。这意味着从我读取的内容中什么都没有返回,因为值只有在Observable处于活动状态时才存在。我现在被困住了。我可以使用“个人档案”搜索组件和“回购列表”组件,但是我需要“个人档案”组件将回购网址传递给“回购列表”组件。

profile-search.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, BehaviorSubject } from 'rxjs';
import { HttpErrorHandler, HandleError } from '../error-handler.service';

export interface PackageInfo {
  login: string
  name: string
  avatar_url: string
  repos_url: string

}

export const searchUrl = 'https://api.github.com/users/';

const httpOptions = {
  headers: new HttpHeaders({
    'x-refresh':  'true'
  })
};

function createHttpOptions(packageName: string, refresh = false) {
    const headerMap = refresh ? {'x-refresh': 'true'} : {};
    const headers = new HttpHeaders(headerMap) ;
    return { headers };
}

@Injectable({
  providedIn: 'root'
})

export class PackageSearchService {
  searchResults: Observable<PackageInfo>;
  private handleError: HandleError;

  constructor(
    private http: HttpClient,
    httpErrorHandler: HttpErrorHandler
   ) {
    this.handleError = httpErrorHandler.createHandleError('Service');
  }

  search (packageName: string, refresh = false): Observable<PackageInfo>
   {
    const options = createHttpOptions(packageName, refresh);
    this.searchResults = this.http.get(searchUrl + packageName, options) as Observable<PackageInfo>;
    return this.searchResults
  }

}

我的第一个想法是创建一个字符串类型的空白变量。

export class PackageSearchService {
      searchResults: Observable<PackageInfo>;
      private handleError: HandleError;
      repoUrl: string;  <== new

      constructor(
        private http: HttpClient,
        httpErrorHandler: HttpErrorHandler
       ) {
        this.handleError = httpErrorHandler.createHandleError('Service');
      }

      search (packageName: string, refresh = false): Observable<PackageInfo>
       {
        const options = createHttpOptions(packageName, refresh);
        this.searchResults = this.http.get(searchUrl + packageName, options) as Observable<PackageInfo>;
        this.repoUrl = this.searchResults.subscribe(userRepo => userRepo.repo_url)
        return this.searchResults
      }

repo-list.component.ts

import { Component, OnInit } from '@angular/core';
import { RepoListComponent } from './repo-list.services';
import { PackageSearchService } from '../profile-search/profile-search.service';
import { RepoList } from '../repoList';
import { Observable, Subscription } from 'rxjs';



@Component({
  selector: 'repo-list',
  templateUrl: './repo-list.html',
  styleUrls: ['../profile-search/profile-search.component.css']
})

export class UsersRepoComponent implements OnInit {
  repo: RepoList[] = [];


  constructor( private repoService : RepoListComponent,
    private searchService: PackageSearchService){}



  ngOnInit(){
    this.getRepoList(); 
}


  getRepoList(): void {
    this.repoService.getRepoReturn()
    .subscribe(repo => this.repo = repo);
  }

  getRepoUrl(){
    this.searchService.repoUrl;
  }

}

5 个答案:

答案 0 :(得分:0)

您可以在服务类中定义具有URL的构造函数,该URL可以进一步使用。下面是代码段

repoUrl:string

constructor(private httpClient:HttpClient) {
  this.repoUrl ="http://localhost:3990/"
}

getProjects(packageName:string):Observable<Project[]>{
   return this.httpClient.get(this.repoUrl+packageName)
}

答案 1 :(得分:0)

您的代码没有多大意义:

  1. 传递给createHttpOptions()的参数不会在任何地方使用
  2. 您将.subscribe(...)的结果分配给类型为string的变量,但是subscribe()返回的是Subscription,而不是字符串。之所以使用Observable,是因为http调用是异步,因此您无法期望在调用httpClient.get()之后立即获得结果。只有在响应可用时,它才会稍后可用,然后可观察对象将其结果通知给其订户。
  3. 您尝试使用searchService.repoUrl字段,而无需调用初始化该字段的方法。

要链接返回Observable的异步调用,最简单的方法是使用switchMap()运算符:

getRepoUrl().pipe(
  switchMap(url => getSomethingUsingTheUrl(url))
).subscribe(something => doWhateverYouWant(something));

这样做的缺点是,每个要加载内容的主题都将首先发出请求以获取存储库URL,然后发出第二个请求以获取内容。

您只能获取一次URL,并使用shareReplay运算符使用单个可观察到的URL对其进行缓存:

class UrlService {
  url$: Observable<string>;

  constructor(httpClient: HttpClient) {
    this.url$ = httpClient.get(...).pipe(shareReplay());
  }
}

以及您的组件中

this.urlService.url$.pipe(
  switchMap(url => getSomethingUsingTheUrl(url))
).subscribe(something => doWhateverYouWant(something));

可观察性很难掌握,但是如果您想正确有效地使用Angular,您肯定需要花一些时间来学习它们。第一步是了解异步性,这是为什么首先出现可观察对象的主要内容。

答案 2 :(得分:0)

因此,JB,我正在尝试遵循您的示例。

url $:可观察;

我得到一个错误,this.url $是对象,不能分配给字符串,所以我做了

url$: Observable<object>;

我还必须将URL和搜索词组合成一个字符串。

export class PackageSearchService {
  searchResults: Observable<PackageInfo>;
  private handleError: HandleError;

  url$: Observable<Object>;
  fulluserUrl: string;

  constructor(
    private http: HttpClient,
    httpErrorHandler: HttpErrorHandler
   ) {
    this.handleError = httpErrorHandler.createHandleError('Service');
    this.url$ = http.get(this.fulluserUrl).pipe(shareReplay());
  }

  search (packageName: string, refresh = false): Observable<PackageInfo>
   {
    const options = createHttpOptions(packageName, refresh);
    this.searchResults = this.http.get(searchUrl + packageName, options) as Observable<PackageInfo>;
    return this.searchResults
  }

  userRepo (packageName: string)
  {
    this.fulluserUrl = searchUrl + packageName;
    return this.fulluserUrl;
  }

}

关于组件示例

this.urlService.url$.pipe(
  switchMap(url => getSomethingUsingTheUrl(url))
).subscribe(something => doWhateverYouWant(something));

我的profile-search.component中有一个类似的switchMap

switchMap(packageName =>
        this.searchService.search(packageName, this.withRefresh)
      )
      ).subscribe(
        user => this.user = user
      );

只是不确定getSomethingUsingTheUrl或doWhateverWant应该是什么,或者我应该如何编辑

答案 3 :(得分:0)

如果您尝试使用第二个可观察调用中的第一个可观察对象的结果,将两个Observables调用依次链接,您将寻求FlatMap:Chaining RxJS Observables from http data in Angular2 with TypeScript

如果您尝试保存其他非立即呼叫的url字符串,则服务中的ReplaySubject是我过去使用的方式。

快速说明:您可以直接返回this.http.get(...),无需将其参数化为“ searchResults”。

答案 4 :(得分:0)

好的,感谢基南和JB。我可以通过点击并使用BehaviorSubject来解决此问题。

MyService

export class ProfileSearchService {
  profile: BehaviorSubject<ProfileInfo> = new BehaviorSubject({ login: '', name: '', avatar_url: '', repos_url: '' });
  repos: BehaviorSubject<any[]> = new BehaviorSubject([]);

  constructor(private http: HttpClient) {
    this.profile.subscribe(({ repos_url }) => {
      if (repos_url) {
        // http request, set repoFetch to return value
        this.http.get(repos_url).pipe(
          tap(repos => this.repos.next(repos as any[]))
        ).subscribe();;
      }
    });
  }

  search (packageName: string) {
    this.http.get(searchUrl + packageName).pipe(
      tap(user => {
        this.profile.next(user as ProfileInfo)
      })
    ).subscribe();
  }
}

MyComponent

export class UsersRepoComponent implements OnInit {
  repo$: Observable<any>;

  constructor(private searchService: ProfileSearchService){}

  ngOnInit() {
    this.repo$ = this.searchService.repos;
  }

}

还要感谢Corey Pyle握住我的手,并带领我逐步完成。