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>
2
Answers
what is the type of tweetsData in twitter.service.ts
Is this function returning a observable ?
If this is not an observable , you can return Observable.of(this.tweetsData)
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: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:
then when you get your data, call
next()
:Then you have your subscribers listen to this observable, like:
That should do it. Further reading from the docs: https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service