Back to overview

Angular GetSingle from Cache

Angular

Usually, we list data collection in a page, and click edit button and pass id for the data, navigate to edit page. Since we already have the data in list, we can reuse it before making a GET single API call to improve user experience.

Way 1: ReplaySubject

import {ReplaySubject} from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class StoragePolicyService {
  constructor(private readonly http: HttpClient) {}

  // in list component, get All call subscribe method, push data to cachedData. This is the cache STORE
  cachedData = new ReplaySubject<StoragePolicy[]>(1);

  getStoragePolicies() {
    return this.http.get<StoragePolicy[]>(`${ENV.apiPrefix}/core/storage-policies?limit=1000&offset=0`);
  }

  getStoragePolicy(id: string) {
    return this.http.get<StoragePolicy>(`${ENV.apiPrefix}/core/storage-policies/${id}`);
  }

  // in single component, emit data from cachedData
  getStoragePolicyFromCache(id: string) {
    const subject = new ReplaySubject<StoragePolicy | undefined>(1);

    this.cachedData.subscribe((res) => {
      subject.next(res.find((p) => p.id === id));
      subject.complete();
    });
    return subject;
  }
}

Get all component:

this.storagePolicyService
  .getStoragePolicies()
  .pipe(takeUntil(this.unsubscribe))
  .subscribe({
    next: (res) => {
      this.data = res;
      this.loading = false;

      // save data to cached STORE
+      this.storagePolicyService.cachedData.next(res);
+      this.storagePolicyService.cachedData.complete();
    },
  });

Get single component:

import {ActivatedRoute, Router} from '@angular/router';
import {StoragePolicyService} from '@oss-shared/services/storage-policy.service';
import {Subject} from 'rxjs';
import {finalize, takeUntil} from 'rxjs/operators';

@Component({})
export class StoragePolicyPublishComponent implements OnInit, OnDestroy {
  constructor(private route: ActivatedRoute, private storagePolicyService: StoragePolicyService) {
    this.storagePolicyId = this.route.snapshot.paramMap.get('id');
  }

  storagePolicyId: string;

  unsubscribe = new Subject();

  loading = false;
  error: CommonError;
  storagePolicy: StoragePolicy;

  ngOnInit() {
    // get data from cached STORE
    this.storagePolicyService.getStoragePolicyFromCache(this.storagePolicyId).subscribe((p) => {
      this.storagePolicy = p;
    });

    // real get single API
    this.getStoragePolicy(this.storagePolicyId);
  }

  getStoragePolicy(id: string) {
    // if no data is found from cache STORE, meaning loading the single page directly, otherwise it's navigated from list.
    if (!this.storagePolicy) {
      this.loading = true;
    }
    this.error = null;

    this.storagePolicyService
      .getStoragePolicy(id)
      .pipe(
        takeUntil(this.unsubscribe),
        finalize(() => (this.loading = false)),
      )
      .subscribe({
        next: (res) => {
          this.storagePolicy = res;
        },
        error: (err) => (this.error = mapError(err)),
      });
  }
}

Way 2: Optional Parameters

http://localhost:4200/form/edit/2;name=Lifejacket;price=48.95

List component:

<button [routerLink]="['/form', 'edit', item.id, {name: item.name, price: item.price}]">Edit</button>

Single component:

export class SingleComponent {
  product: any;

  constructor(activeRoute: ActivatedRoute) {
    const id = activeRoute.snapshot.params['id'];

    if (id != null) {
      this.product.name = activeRoute.snapshot.params['name'];

      let price = activeRoute.snapshot.params['price'];
      this.product.price == Number.parseFloat(price);

      this.productForm.patchValue(this.product);
    }
  }
}