manumanuk
manumanuk

Reputation: 53

How to handle asynchronous nature of RxJS Observables inside Angular

I'm new to Angular and the Observables architecture, and I'm having trouble running functions that have asynchronous code.

In the code below, I am using the AngularFire library to obtain information from a basic Firebase Realtime Database. However, when I call the function searchForCompany() from one of my components, the observable doesn't load the required data instantly, meaning the HTML elements of my component don't get loaded properly. Am I using the Observables architecture wrong?

import { SubscriptionData } from './interface'
import { Injectable } from '@angular/core';
import { AngularFireDatabase, AngularFireList } from '@angular/fire/database'

@Injectable({
  providedIn: 'root'
})

export class SubscriptionService {
  private dbRead: SubscriptionData[];
  private subscriptionData = new SubscriptionData;
  private databasePath = "/companies";
  private databaseRef: AngularFireList<SubscriptionData>;

  constructor(private _database: AngularFireDatabase) {
    this.databaseRef = _database.list(this.databasePath);
  }

  searchForCompany(service:String) {
    for (let key of this.dbRead) {
      //Search for name of a company in the Firebase database
      if (key.name == service) {
        this.subscriptionData=key;
        return;
      }
    }

  readDatabase() {
    this.databaseRef.valueChanges().subscribe(data=> {
      this.dbRead = data;
    })
  }
}

Upvotes: 1

Views: 156

Answers (1)

Dmitrii Makarov
Dmitrii Makarov

Reputation: 759

think about observable like about a stream that provides data. Your component should subscribe to this stream. Then a component will receive every new value from the stream.

Some example here:

@Component({
  selector: 'app-users-listing',
  templateUrl: './users-listing.component.html',
  styleUrls: ['./users-listing.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UsersListingComponent {

  // THIS IS A "STREAM" AND YOU NEED TO SUBSCRIBE TO IT IN THE TEMPLATE OR INSIDE CODE
  users$: Observable<UserInterface[]> = this.activatedRoute.data.pipe(
    map(data => data.users),
  );

  constructor(
    private activatedRoute: ActivatedRoute,
  ) { }

}

Let's subscribe to users$ inside the template

<mat-list>
  <!-- "users$ | async" creates a subscription  -->
  <mat-list-item *ngFor="let user of (users$ | async)">
    <h3 matLine> {{ user.first_name + ' ' + user.last_name }} </h3>
    <p matLine>
      <span> {{user.email}} </span>
    </p>
  </mat-list-item>
</mat-list>

Regarding to your example. Try to change your service to return an Observable from the method and subscribe to it in your component. It could be like

searchForCompany () {
  return this.databaseRef.valueChanges()
}

And subscribe to it in the component, transform received data, etc. It may looks like:

export class MyComponent {
  constructor (private service: SubscriptionService) {}

  ngOnInit(): void {
    this.service.searchForCompany().pipe(
      // SOME ADDITIONAL LOGIC
      // --> WRITE SOME CODE TO DESTROY SUBSCRIPTION HERE <---
    ).subscribe();
  }
}

OR create class property - observable. And subscribe to it in template using async pipe

export class MyComponent {

  propert$ = this.service.searchForCompany().pipe(
    // SOME ADDITIONAL LOGIC
  )
  constructor (private service: SubscriptionService) {}
}

Hope it helps.

Upvotes: 1

Related Questions