user3259245
user3259245

Reputation: 64

Observable caching and cache clearing without invalidating subscriptions in Angular

I have two components that both use data retrieved from an API endpoint over http. I want to cache the response from the API endpoint so only one http request is made, but I also want to be able to clear the cache when I know data has changed so that the two components are updated via their original subscriptions.

I can get caching to work so only one http request is made by using publishReplay(1). But I can't find a way to invalidate the cache with that method.

I've tried creating my own observable and calling next() when I don't already have a cached version of the data, but I can't figure out how I might redefine the observer function so it returns different data when I invalidate the cache.

class ApiService {
  private user$: Observable<User>;
  ...
  getUser() {
    if (!this.user$)
      this.user$ = this.http.get<User>(`${apiUrl}users/me`).pipe(shareReplay(1));
    return this.user$;
  }

  invalidateUserCache() {
    // do what? Can't assign a new observable to this.user$ because 
    // any components that have already subscribed won't be notified
    // of the updates
  }
}

class UserProfileComponent implements OnInit {
  user: User;
  ...
  ngOnInit() {
    this.api.getUser().subscribe(user => this.user = user);
  }
  updateUser() {
    this.api.updateUser(someData).subscribe(response => {
      this.api.invalidateUserCache();
      // Since cache has been invalidated, I want this to 
      // make a new http request and notify other components 
      // that have subscribed.
      this.api.getUser().subscribe(user => this.user = user);
    }
  }
}

class MenuComponent implements OnInit {
  user: User;
  ...
  ngOnInit() {
    this.api.getUser().subscribe(user => this.user = user);
  }
}

The above code caches the User object so only one http request is made, but if UserProfileComponent updates the user, MenuComponent is not updated with the new user.

Upvotes: 2

Views: 1697

Answers (2)

user3259245
user3259245

Reputation: 64

At Jeyenth's suggestion of using BehaviorSubject, I came up with this:

class ApiService {
  private _user: BehaviorSubject<User>;
  ...
  getUser() {
    if (!this._user) { // we haven't already requested the current user
      this._user = new BehaviorSubject(null);
      this.http.get<User>(`${apiUrl}users/me`).subscribe(user => this._user.next(user));
    }
    return this._user;
  }

  refreshUser() {
    this.http.get<User>(`${apiUrl}users/me`).subscribe(user => this._user.next(user));
  }
}

It works perfectly. Thanks Jeyenth!

Upvotes: 1

S.Voulgaris
S.Voulgaris

Reputation: 314

If you want to update the cache, you simply set the reference value this.user$ to null. Then when the method getUser() is executed again, it will run a new server request which then updates the previous stored cache.

 clearCache() {
    this.user$= null;
 }

Upvotes: 0

Related Questions