NgRX 8 and Firestore in realtime

NgRX is a is a statemanagement library which follows the Redux pattern. This means that there is a state for the entire application stored in the NgRX store. These store can't be modified directly (immutable), but it can change if the user dispatches an action. The store listens to these actions. Actions can be for example the toggling of the sidenav, changing the route, requesting http data, incoming http data on success or error and so on.

One part of the NgRX library are the effects. Side-effects can be used for example for http requests. The user can trigger the loadComments action. The side-effect of this action could be loading the comments from the server. If the answer arrives another action will be dispatched, maybe loadCommentsSuccess. Effects can not only be triggered by actions, but by any observable.

And this is where the firebase observable comes into play. It provides the valueChanges function, which returns an observable of the serverdata and emits in realtime. What we want to do is to dispatch new actions, when this observable emits. Therefore we are implementing the comments.service.ts beforehand. Inject the AngularFirestore into your service.

constructor(private db: AngularFirestore) {}

Then we can implement the get function. The get function is pretty similar to a REST http function. The difference is, that the observable does not complete after the first data arrives. It is alive until someone unsubscribes from it.

get(): Observable<Comment[]> {
  const commentsCollection = this.db.collection('comments');
  return commentsCollection.valueChanges();
}

And now we can use that observable inside the effects. Instead of listening to the loadComments action, we are listening to server changes. If the observable succeeds, we are dispatching the loadCommentsSuccess action, otherwise the loadCommentsFailed action.

import { loadCommentsSuccess, loadCommentsFailed } from '../actions';

@Injectable()
export class CommentsEffects {
  constructor(private commentsService: CommentsService) {}

  syncProjects = createEffect(() =>
    this.commentsService.get().pipe(
      map((comments) => loadCommentsSuccess({ comments })),
      catchError((error) => of(loadCommentsFailed({ error })))
    )
  );
}

This is actually pretty simple, but since there is almost no article out there showing that one can use "observables" instead of actions only inside the effect, I thought it would be good to have it documented publically.