2

I am coding a Single Page Application in Angular 5 and it requires to periodically check if API still has the same version as when the SPA was loaded to inform the user about the desync and allow her to refresh (the first call must be performed as soon as possible).

I have little experience working with rxjs and I know that if I am not careful I might end with unsubscribed observables and/or more HTTP calls than required.

My current code looks like this:

The service

import { Injectable } from "@angular/core";
import { IValidationResult } from "../models/validation-result";
import { Subscription } from "rxjs/Subscription";
import { Observable } from "rxjs/Observable";
import { HttpClient } from "@angular/common/http";

@Injectable()
export class KeepAliveService {

  checkVersion$: Observable<IValidationResult<string>>;
  checkVersionSubscription: Subscription;

  constructor(
    private readonly httpClient: HttpClient) {
  }

  isUpToDate: boolean;
  isLive = true;
  liveVersion: string;

  performCheckVersion(): void {

    const localVersion = localStorage.getItem("MyAppLocalVersion");

    this.checkVersion$ = this.httpClient.get<IValidationResult<string>>("GetApiVersion");

    this.checkVersionSubscription = this.checkVersion$.subscribe(
      (result: IValidationResult<string>) => {
        this.liveVersion = result.payload;
        this.isUpToDate = localVersion === this.liveVersion;
        this.isLive = true;

        this.checkVersionSubscription.unsubscribe();
    });
  }
}

Refreshing code (app.module.ts)

console.log("Quick version check");
this.keepAliveService.performCheckVersion();

console.log("Set check version setInterval");
setInterval(() => {
  this.keepAliveService.performCheckVersion();
}, this.checkVersionInterval);

This does the job, but I feel it seems rather complex for such a simple job. Is there a "Observableish" way of obtaining the same functionality?


I have tried the following:

The service

import { Injectable } from "@angular/core";
import { IValidationResult } from "../models/validation-result";
import { Subscription } from "rxjs/Subscription";
import { Observable } from "rxjs/Observable";
import { HttpClient } from "@angular/common/http";
import { interval } from "rxjs/observable/interval";
import { switchMap } from "rxjs/operator/switchMap";

  startCheckingVersion() {

    const httpObservable = interval(this.checkVersionInterval)
      .switchMap(_ => this.httpClient.get<IValidationResult<string>>("GetApiVersion"));  

      this.checkVersionSubscription = httpObservable.subscribe(
        (result: IValidationResult<string>) => {
          const localVersion = localStorage.getItem("MyAppLocalVersion");
          this.liveVersion = result.payload;
          this.isUpToDate = localVersion === this.liveVersion;
          this.isLive = true;
        });
  }

However, there is an issue related to how I use timer and switchMap as I received the following error:

timer_1.timer(...).switchMap is not a function ; Zone: ; Task: Promise.then ; Value: TypeError: timer_1.timer(...).switchMap is not a function

My package.json rxjs version:

"rxjs": "^5.5.6",
Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164
  • @jcuypers - I obtain `httpObservable` which is of type `Observable>` and I subscribe to it two lines below: `this.checkVersionSubscription = httpObservable.subscribe( ...` – Alexei - check Codidact Mar 12 '19 at 08:54
  • yes, I understood. but I didn't understand how the httpclient call itself got subscribed (by the first subscribe you only activate the interval)... this should be done automatically by switchmap (the actual http call subscribe) – jcuypers Mar 12 '19 at 08:57
  • 1
    please refer to this : https://stackoverflow.com/questions/50200859/i-dont-get-rxjs-6-with-angular-6-with-interval-switchmap-and-map – jcuypers Mar 12 '19 at 08:59
  • 1
    @jcuypers - although the referenced question is for rxjs 6 (and I am using 5.5), the imports and pipe are working fine for me. Thanks. – Alexei - check Codidact Mar 12 '19 at 09:05
  • sorry, missed that. but ok great – jcuypers Mar 12 '19 at 09:06

1 Answers1

1

jcuypers correctly showed me how to make it work and the initial code is greatly reduced:

the service

import { Injectable } from "@angular/core";
import { IValidationResult } from "../models/validation-result";
import { Subscription } from "rxjs/Subscription";
import { Observable } from "rxjs/Observable";
import { HttpClient } from "@angular/common/http";

// had to pay attention to the imports
import { switchMap } from "rxjs/operators";
import { timer } from "rxjs/observable/timer";

@Injectable()
export class KeepAliveService {

  readonly checkVersionInterval = 30000;

  constructor(
    private readonly httpClient: HttpClient) {
  }

  isUpToDate: boolean;
  isLive = true;
  liveVersion: string;

  startCheckingVersion() {

    // use pipe with switchMap to perform the http call at time 0 + every some interval
    const httpObservable = timer(0, this.checkVersionInterval)
      .pipe(switchMap(_ => this.httpClient.get<IValidationResult<string>>("GetApiVersion")));

      this.checkVersionSubscription = httpObservable.subscribe(
        (result: IValidationResult<string>) => {
          const localVersion = localStorage.getItem("MyAppLocalVersion");
          this.liveVersion = result.payload;
          this.isUpToDate = localVersion === this.liveVersion;
          this.isLive = true;
        });
  }
Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164