Storing a fetched list of printers in Angular
"Caching" an Observable

PrintersService is by default a singleton in Angular just like any other service (This is thanks to the way dependency injection container works in Angular). This means you don't need to have the class fields as static. Make them instance fields instead:

private readonly apiUrl = "/api/printers";
private printers: Observable&lt;Printer[]&gt; = undefined;

updatePrinter(printer: Printer) {     
  this.printers.subscribe(printers =&gt; {
    printers.forEach((element, index) =&gt; {
      if (element.id === printer.id) {
        printers[index] = printer;
        this.http.put(this.apiUrl, printer).pipe(
          tap(_ =&gt; this.log(`Updated printer id=${printer.id}`)),
          catchError(this.handleError&lt;any&gt;('updatePrinter'))
        );
      }
    });
  });
}

private apiGET(): boolean {    
  this.log("GET: " + this.apiUrl);
  this.printers = this.http.get&lt;Printer[]&gt;(PrintersService.apiUrl);
  return PrintersService.printers === undefined;
}


Caching Pattern

I commonly see the following pattern for "caching" observables in various code bases:

const CACHE_SIZE = 1;

this.cache$ = this.requestJokes().pipe(
  shareReplay(CACHE_SIZE)
);


It will not very nicely with the code you have because the pattern addresses the GET calls assuming there's no mutation to the list of Jokes (Printers in your case) such as PUTs, POSTs or DELETEs.

Alternative 1: Observable as BehaviorSubject

You can use a BehaviorSubject&lt;Printer[]&gt; to deal with the state manually. BehaviorSubject is an observable and an observer at the same time. It has methods next(newValue) and getValue() which make manual state control possible.

Alternative 2: More advanced state management

I've been where you are many times. The complexity of manual observable/behaviour subject led me to a conclusion the approach is not scaling well. It holds me back from actually progressing on feature work itself.

My recommendation is to look at a state management library like @ngrx/store. The library ideas revolve around a few concepts one which is Store. The store represents the state of your application that is exposed as an observable. The operations that change (update) the stored are written in a special way (via so called "reducers").

In my experience, the apps written with @NgRx are simpler in building and even simpler in maintenance. You'll need invest some time in picking up the library's approach, API, and so on but the overall entry barrier is relatively low.



Side notes


Use interface instead of class if possible.
Do not use keywords as field names if possible, (i.e. default).
Use is* or are* in member names to denote the boolean nature of the member. (I've seen word "default" used as a verb with the meaning of reset to default(s)).




export interface Printer
{
  id: number;
  name: string;
  isActive: boolean;
  isBlacklisted: boolean;
  isDefault: boolean;
}