stijn26 stijn26 - 2 months ago 15
HTTP Question

Cache data but check in the background for new data

I want to cache my data, but at the same time I need my data to be up-to-date. I found this: Angular2 easiest way to cache HTTP responses but this will not check for new data.

I have this now in my service:

public publishedSessions: Session[] = null;

getPublishedSessions(): Observable<any> {
let headers = new Headers();
headers.append('authorization', this.userService.getToken());

if (this.publishedSessions) {
this.http.get(this.apiUrl + 'api/sessions/published', {
headers: headers
})
.map(res => res.json().sessions)
.subscribe(sessions => this.publishedSessions = sessions);

return Observable.of(this.publishedSessions);
} else {
return this.http.get(this.apiUrl + 'api/sessions/published', {
headers: headers
})
.do(res => this.publishedSessions = res.json().sessions)
.map(res => res.json().sessions)
.catch((error) => Observable.of(error));
}
}


And some standard code in my component:

handlePublishedSessions(): void {
this.subscriptionArr.push(this.sessionService.getPublishedSessions().subscribe(sessions => {
this.session = sessions
}));
}


This causes the effect that when I first navigate (visit 1) to the page, a call (call 1) will be made (wanted). Then if I navigate away and return back to the page (visit 2), the data from call 1 will be returned (not wanted), in the meantime, call 2 is in the works. So if I then navigate away again and navigate back (visit 3), the data from call 2 is being returned.

I want that the call 1 data is displayed on visit 2 for the first few milliseconds (untill call 2 is done). When call 2 is done I want the data to be replaced (without user interaction).

Answer

I would use a BehaviorSubject to cache data.

Take a look at this plunker to get an idea: https://plnkr.co/edit/jNNQJToYia2MhIE488YX?p=preview

import {Component, NgModule, Injectable} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import {HttpModule, Http} from '@angular/http';

import {BehaviorSubject} from 'rxjs/Rx';

@Injectable()
export class AnyService {

  public data = new BehaviorSubject<string>();

  constructor(private _http: Http) { }

  public getData(): string {
    this._http.get('https://httpbin.org/bytes/12')
      .subscribe(
        resp => this.data.next(resp._body)
      );
    return this.data.value;
  }
}

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2 (click)="getData()">Hello {{name}} -- CLICK ME !! --</h2>
    </div>
  `,
})
export class App {
  name:string;

  firstSubscribeCallback = false;

  constructor(private _srvc: AnyService) {
    this.name = 'Angular2'

    this._srvc.data.subscribe(
      newData => {

        // FIRST CALL WILL BE THE CACHED DATA..
        if (!this.firstSubscribeCallback) { // JUST FOR DEMO ..

          console.log('got cached data @ startup..');
          this.firstSubscribeCallback = true;

        }
        else console.log('got new data:');

        console.log(newData);
      }
    );

    this.getData(); // get FRESH data ..
  }

  getData() {
    console.log('getting cached data:');
    console.log(this._srvc.getData());
  }
}

@NgModule({
  imports: [ BrowserModule, HttpModule ],
  declarations: [ App ],
  providers: [ AnyService ],
  bootstrap: [ App ]
})
export class AppModule {}
Comments