访问服务的异步变量 [英] Accessing async variable of service

查看:15
本文介绍了访问服务的异步变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我第一个 Angular 4 项目的一部分.我目前可以从搜索栏中很好地调用 searchCall 函数,但是存储在 tweetsData 中的数据似乎不在我在 app.component.html 中的 *ngFor 调用的范围内,并且作为异步后端调用推特 api.我收到此错误:TypeError:无法读取未定义的属性订阅",因此我不能正确设置可观察的设置.非常感谢任何帮助.

twitter.service.ts

import { Injectable } from '@angular/core';从 '@angular/http' 导入 { Http, Headers };从 'rxjs/Observable' 导入 { Observable };从rxjs/主题"导入{主题};@Injectable()导出类 TwitterService {搜索查询:字符串 = '';推文数据;构造函数(私有http:Http){console.log('TwitterService 已创建');this.getBearerToken();}getBearerToken() {//从 twitter api 获取不记名令牌var headers = new headers();headers.append('Content-Type', 'application/x-www-form-urlencoded');this.http.post('http://localhost:3000/authorize', {headers: headers}).subscribe((res) => {控制台日志(res);});}getTweetsData():Observable{返回 this.tweetsData;}搜索调用(){console.log('searchCall made');控制台日志(this.searchQuery);var headers = new headers();var searchTerm = 'query=' + this.searchQuery;headers.append('Content-Type', 'application/x-www-form-urlencoded');this.http.post('http://localhost:3000/search', searchTerm, {headers: headers}).subscribe((res) => {this.tweetsData = res.json().data.statuses;});}}

app.component.ts

import { Component, OnInit, TemplateRef } from '@angular/core';从 '@angular/http' 导入 { Http, Headers };从 './twitter.service' 导入 { TwitterService };从 'ngx-bootstrap/modal' 导入 { BsModalRef, BsModalService };@成分({选择器:'app-root',templateUrl: './app.component.html',styleUrls: ['custom-styles/app.component.css'],提供者:[TwitterService]})导出类 AppComponent 实现 OnInit {搜索查询:字符串 = '';推文数据;expandNewTweetBox:boolean = false;公共 modalRef: BsModalRef;构造函数(私有 http:Http,私有 twitterService:TwitterService,私有 modalService:BsModalService){}ngOnInit() {this.twitterService.getTweetsData().subscribe(数据 =>this.tweetsData = 数据)}公共 openModal(模板:TemplateRef){this.modalRef = this.modalService.show(template);}}

app.component.html

<预><代码>...<div *ngIf="tweetsData"><div *ngFor="let item of tweetsData" class="col-12">...

navbar.component.html

<input type="text" class="form-control" placeholder="Search Chiller" aria-label="Search Chiller" [(ngModel)]="twitterService.searchQuery" [ngModelOptions]="{standalone: true}><span class="input-group-btn"><button class="btn btn-secondary" type="button" (click)="twitterService.searchCall()">Go!</button></span>

解决方案

第一个问题,就像评论中指出的那样,在组件级别拥有您的 providers 数组将意味着您有单独的服务实例,因此它根本不共享.所以删除那些!

你也有竞争条件,就像评论中提到的一样.

我知道您希望订阅者在 tweetsData 有值时收听.您需要做的是为这些订阅者提供可观察的您现在正在做什么:

getTweetsData():Observable{返回 this.tweetsData;}

返回一个数组(假设),而不是一个可观察的数组.您不能订阅常规"数组.

所以我要做的是在服务中声明一个 Observable:

import { BehaviorSubject } from 'rxjs/BehaviorSubject';//不要使用'any',而是输入你的数据//如果订阅者一直在收听,您也可以使用主题"private tweetsData = new BehaviorSubject(null);公共 tweetsData$ = this.tweetsData.asObservable();

然后当你得到你的数据时,调用next():

searchCall() {//....this.http.post(...).subscribe((res) => {this.tweetsData.next(res.json().data.statuses)});}

然后你让你的订阅者收听这个 observable,比如:

constructor(private twitterService: TwitterService) {twitterService.tweetsData$.订阅(数据=> {this.tweetsData = 数据;});}

应该可以.进一步阅读文档:https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service

This is part of my first Angular 4 project. I am currently able to call the searchCall function just fine from a search bar, but the data being stored in tweetsData doesn't seem to be in scope with my *ngFor call in app.component.html, as well as being an asynchronous backend call to the twitter api. I get this error: TypeError: Cannot read property 'subscribe' of undefined, so I must not have the observable setup correctly. Any help is greatly appreciated.

twitter.service.ts

import { Injectable } from '@angular/core';
import { Http, Headers } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class TwitterService {
  searchQuery: string = '';
  tweetsData;

  constructor(private http: Http) {
    console.log('TwitterService created');
    this.getBearerToken();
  }

  getBearerToken() {
    // get bearer token from twitter api
    var headers = new Headers();

    headers.append('Content-Type', 'application/x-www-form-urlencoded');

    this.http.post('http://localhost:3000/authorize', {headers: headers}).subscribe((res) => {
      console.log(res);
    });
  }

  getTweetsData():Observable<any> {
    return this.tweetsData;
  }

  searchCall() {
    console.log('searchCall made');
    console.log(this.searchQuery);
    var headers = new Headers();
    var searchTerm = 'query=' + this.searchQuery;

    headers.append('Content-Type', 'application/x-www-form-urlencoded');

    this.http.post('http://localhost:3000/search', searchTerm, {headers: headers}).subscribe((res) => {
      this.tweetsData = res.json().data.statuses;
    });
  }

}

app.component.ts

import { Component, OnInit, TemplateRef } from '@angular/core';
import { Http, Headers } from '@angular/http';

import { TwitterService } from './twitter.service';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: [ 'custom-styles/app.component.css' ],
  providers: [TwitterService]
})
export class AppComponent implements OnInit {
  searchQuery: string = '';
  tweetsData;
  expandedNewTweetBox: boolean = false;

  public modalRef: BsModalRef;

  constructor(private http: Http, private twitterService: TwitterService, private modalService: BsModalService) {}

  ngOnInit() {
    this.twitterService.getTweetsData().subscribe(
      data => this.tweetsData = data
    )
  }

  public openModal(template: TemplateRef<any>) {
    this.modalRef = this.modalService.show(template);
  }
}

app.component.html

...
<div *ngIf="tweetsData">
  <div *ngFor="let item of tweetsData" class="col-12">
...

navbar.component.html

<div class="input-group">
   <input type="text" class="form-control" placeholder="Search Chiller" aria-label="Search Chiller" [(ngModel)]="twitterService.searchQuery" [ngModelOptions]="{standalone: true}">
   <span class="input-group-btn">
      <button class="btn btn-secondary" type="button" (click)="twitterService.searchCall()">Go!</button>
   </span>
</div>

解决方案

First problem, like pointed out in comments, having your providers array at component level will mean that you have separate instances of services, so it's not shared at all. So remove those!

Also you have race conditions, like also mentioned in comments.

I understand that you want to have subscribers listen to when tweetsData has values. What you need to do, is provide these subscribers with observables What you are doing now:

getTweetsData():Observable<any> {
  return this.tweetsData;
}

returns an array (assumingly), not an observable of an array. You cannot subscribe to a "regular" array.

So what I would do, is to declare an Observable in the service:

import { BehaviorSubject } from 'rxjs/BehaviorSubject';

// don't use 'any', type your data instead
// you can also use a 'Subject' if the subscribers are always listening
private tweetsData = new BehaviorSubject<any>(null);
public tweetsData$ = this.tweetsData.asObservable();

then when you get your data, call next():

searchCall() {
  // ....
  this.http.post(...)
    .subscribe((res) => {
      this.tweetsData.next(res.json().data.statuses)
    });
}

Then you have your subscribers listen to this observable, like:

constructor(private twitterService: TwitterService) {
  twitterService.tweetsData$
    .subscribe(data => {
       this.tweetsData = data;
    });
}

That should do it. Further reading from the docs: https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service

这篇关于访问服务的异步变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆