5
\$\begingroup\$

There are different methods to get data from server in Angular application:

  1. Get Observable from the service and subscribe to it at component
  2. Create Subject at the service and subscribe to the Subject at component

Both of this methods work for me but I can't understand which should I use.

First method. Get Observable from the service and subscribe to it at component:

article.service.ts

import { Injectable } from '@angular/core';
import { Article } from '../models/article';
import { AngularFirestore } from '@angular/fire/firestore';
import { map, take } from 'rxjs/operators';
import { Observable, Subject } from 'rxjs';

@Injectable({
  providedIn: "root"
})
export class ArticleService {
  public articlesChanged: Subject<Article[]> = new Subject<Article[]>();
  articles: Article[];

  constructor(private db: AngularFirestore) {}

  get() {
    return this.db.collection('articles').valueChanges({ idField: 'id' });
  }
}

home.component.ts

import { Component, OnInit } from '@angular/core';
import { ArticleService } from 'src/app/services/article.service';
import { Observable, Subscription } from 'rxjs';
import { Article } from 'src/app/models/article';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})

export class HomeComponent implements OnInit {
  articles: Article[];

  constructor(private articlesService: ArticleService) { }

  ngOnInit() {
    this.articlesService.get().subscribe(articles => this.articles = articles as Article[]);
  }
}

Second method. Create Subject at the service and subscribe to the Subject at component:

article.service.ts

import { Injectable } from '@angular/core';
import { Article } from '../models/article';
import { AngularFirestore } from '@angular/fire/firestore';
import { map, take } from 'rxjs/operators';
import { Observable, Subject } from 'rxjs';

@Injectable({
  providedIn: "root"
})
export class ArticleService {
  public articlesChanged: Subject<Article[]> = new Subject<Article[]>();
  articles: Article[];

  constructor(private db: AngularFirestore) {}

  get(): void {
    this.db
      .collection('articles')
      .valueChanges({ idField: 'id' }).subscribe(articles => {
        this.articles = articles as Article[];
        this.articlesChanged.next(this.articles);
      });
  }
}

home.component.ts

import { Component, OnInit, OnDestroy } from '@angular/core';
import { ArticleService } from 'src/app/services/article.service';
import { Observable, Subscription } from 'rxjs';
import { Article } from 'src/app/models/article';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})

export class HomeComponent implements OnInit, OnDestroy {
  articlesSubscription: Subscription;
  articles: Article[];

  constructor(private articlesService: ArticleService) { }

  ngOnInit() {
    this.articlesSubscription = this.articlesService.articlesChanged.subscribe(articles => this.articles = articles);
    this.articlesService.get();
  }

  ngOnDestroy(): void {
    this.articlesSubscription.unsubscribe();
  }
}

Is there a best practice which I should use?

\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

I suggest to use the first approach. You than can provide some pipes that could transform the returned values as you need. And if the articles are showed only in your component is better to store them inside then as a service property, because they will be stored even after the component will be destroyed. In an multipage application it can cause many problem to store old data.

Your second approach you also leave an observable (the one listening for changes) running after the component destroy.

\$\endgroup\$
2
\$\begingroup\$

I think at the end it depends on the use case.
For a simple loading of data, i would use the approach of getting the Observable directly. When the backendcall is not anymore the single source of changes (for example when i could change the data in the frontend and persist it later), or when i want to "store" the data, so that i do not have to call the backend for each request, then i would start thinking about providing an extra observable.

But i would not make the "storage" public, because then others would be able to emit values on it. I would make it private and provide a method that gives access to the data.

// ...
private articles$ : BehaviorSubject<Article[]>([]);

public getArticles(): Observable<Article[]> {
   return this.articles$.pipe(
     distinctUntilChanged();
   )
}

public loadArticles(): void {
  this.db.collection('articles').valueChanges({ idField: 'id' })
  .subscribe( 
    (articles: Article[]) => this.articles.next(articles)
  );
}

In my experience i worked mostly with a direct connected backend call. Perhaps with a pipe to map the backend data format into my frontend data format, but thats it.
The other approach i use mostly when i want to implement a central data source.

By the way, i have the tendency to type the return types of my methods, so that the transpiler can provide me some support. :-)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.