0
votes

I am new to Angular5 and I am right now using Firebase to fetch data for displaying my page. I googled a few examples of how to get data from firebase using AngularFire but a lot of them are out-dated. And finally I figured out I should use Observables and subscribe it.

service.ts

 constructor(private db: AngularFireDatabase) {
  }

  shows: Show[] = [];

  getShows(): Show[] {
    const response = (this.db.list('show/data')
                     .valueChanges() as Observable<Show[]>)
                     .subscribe(
                        data => {
                          data.forEach(element => {
                            console.log(element);
                            console.log(typeof element);
                            this.shows.push(element);
                          });
                        }
                      );
    console.log(this.shows);
    return this.shows;
  }

I console.log both the data I fetched from subscribe() and the data out of the subscribe() function. And the second console.log came out first with an empty [] and the console.log in the subscribe() came out later with each individual object I got from firebase.

Theoretically, as the time I run the second console.log, this.shows is empty and when it returned no data was fetched and the page should not display anything. However, in the app component, when I run the service to get shows, i can still get the information I want and the pages were displayed as expected. Why this happened? How can I make sure when the return functions executed, this.shows is empty or not?

Also, I revised my code like this.

//Service.ts

constructor(private db: AngularFireDatabase) {
  }

  shows: Observable<Show[]>;

  getShows(): Observable<Show[]> {
    const response = (this.db.list('show/data')
                     .valueChanges() as Observable<Show[]>);
    return response;
  }

// show.component.ts

shows: Show[] = [];

  ngOnInit() {
    this.getShows();
  }

  getShows(): void {
    this.showService.getShows().subscribe(
      data => {
        data.forEach(element => {
          console.log(element);
          console.log(typeof element);
          this.shows.push(element);
        });
      },
      err => console.error('Observer got an error: ' + err)
    );
  }

It gave the error of type observable is not assignable to type 'Show[]'. Property 'includes' is missing in type 'Observable'. How can I fix this?

1
Welcome to the wonderful world of asynchronous processing. At the time the second console.log() executes, your asynchronous request has not yet finished executing. - Robby Cornelissen
I understand this. My question is how can i figure out that the when I execute the return function, my this.shows is empty or not. I mean, if the time the return function has executed with an empty [] of this.shows, nothing should display on my front-end page. - fancylynn

1 Answers

0
votes

Observables are the core object in the library RxJs, Reactive Extensions Library for JavaScript https://rxjs-dev.firebaseapp.com/. Angular is built on observables and it is a very important concept in learning Angular. Basically an observable is like an asynchronous array where the elements come in a sequence over time. When you subscribe to an Observable each time a new element comes down the stream your subscription function gets run. It is very much like a JavaScript or jQuery promise but they can be long lived with many items coming down the stream over time.

It can be a confusing subject to start with but one of the most helpful resources I found in learning RxJs was http://rxmarbles.com/.

You really do need to learn the concepts behind RxJs and Observables to be a good Angular developer and it is a topic much bigger than can be covered in a single StackOverflow question.

In your case you have registered a subscription function to get called when the call to the server has received a response. The second console.log gets called first because the function is only run once the response from the server has been received.

I would suggest you look into the async pipe. You can assign the Observable to a property of your component in TypeScript like this

show$ Observable<Show[]> = this.db.list('show/data');

and then in your view you can manage the subscription with the async pipe and assign the value to a view variable like

<ng-container *ngIf="show$ | async as shows">
    <div *ngFor="let show of shows">
        {{show}}
    </div>
</ng-container>

That way you don't have to worry about managing subscriptions in your TypeScript and your list of shows will magically appear once the call to the server has finished.