How to do caching in Angular 17?
Welcome back to AyyazTech! In today's post, we're diving deep into the world of caching in Angular 17. Effective caching can significantly enhance the performance of your Angular applications by reducing unnecessary HTTP requests and optimizing data retrieval. In this guide, we'll explore various caching techniques, including using services, observables, interceptors, and local storage.
Introduction to Caching in Angular
Caching is a critical aspect of web development that helps in storing data temporarily to serve future requests faster. Angular 17 offers multiple methods to implement caching, which we will cover in this article. By the end of this post, you'll have a solid understanding of how to optimize your Angular applications with efficient caching mechanisms.
Setting Up Your Angular Project
Before we start, ensure you have an Angular project set up. If not, create a new Angular project using the following command:
ng new angular-caching
1ng new angular-caching
Navigate to your project directory and open it in your preferred code editor. For this tutorial, we'll use Visual Studio Code.
Implementing Caching with Services and Observables
First, we'll implement a simple caching mechanism using Angular services and observables. This method is straightforward and perfect for beginners.
Creating a Caching Service
Generate a caching service that will handle the storage and retrieval of cached data:
ng generate service cache
1ng generate service cache
In the generated service, define a private property to store the cache:
private cache = new Map<string, any>();
1private cache = new Map<string, any>();
Create get
and set
methods to manage the cache:
get(key: string): Observable<any> | null { return this.cache.has(key) ? of(this.cache.get(key)) : null; } set(key: string, value: any): void { this.cache.set(key, value); }
1get(key: string): Observable<any> | null {
2 return this.cache.has(key) ? of(this.cache.get(key)) : null;
3}
4set(key: string, value: any): void {
5 this.cache.set(key, value);
6}
Using the Caching Service in Data Service
Next, generate a data service that will utilize the caching service:
ng generate service data
1ng generate service data
Inject the HTTP client and caching service into the data service and create a method to fetch data:
@Injectable({ providedIn: 'root' }) export class DataService { constructor(private http: HttpClient, private cacheService: CacheService) {} getData(url: string): Observable<any> { const cachedResponse = this.cacheService.get(url); if (cachedResponse) { return cachedResponse; } return this.http.get(url).pipe( tap(response => this.cacheService.set(url, response)) ); } }
1@Injectable({
2 providedIn: 'root'
3})
4export class DataService {
5 constructor(private http: HttpClient, private cacheService: CacheService) {}
6 getData(url: string): Observable<any> {
7 const cachedResponse = this.cacheService.get(url);
8 if (cachedResponse) {
9 return cachedResponse;
10 }
11 return this.http.get(url).pipe(
12 tap(response => this.cacheService.set(url, response))
13 );
14 }
15}
16
Displaying Data in a Component
Generate a component to display the cached data:
ng generate component articles
1ng generate component articles
In the component, inject the data service and load data using the caching mechanism:
export class ArticlesComponent implements OnInit { articles: any[] = []; constructor(private dataService: DataService) {} ngOnInit() { this.loadArticles(); } loadArticles() { this.dataService.getData('https://api.example.com/articles') .subscribe(data => this.articles = data); } }
1export class ArticlesComponent implements OnInit {
2 articles: any[] = [];
3 constructor(private dataService: DataService) {}
4 ngOnInit() {
5 this.loadArticles();
6 }
7 loadArticles() {
8 this.dataService.getData('https://api.example.com/articles')
9 .subscribe(data => this.articles = data);
10 }
11}
12
Importing ArticlesComponent in app.component.ts
Now to view this component in browser, we need to add it in app.component.html. But before that we need to import the ArticlesComponent in the imports array of app.component.ts. Please note that I am using Angular 17 standalone components in this tutorial hence you can directly import the ArticlesComponent in app.component.ts instead of declaring in any module.
Now add the <app-articles><app-articles> element in the app.component.html.
Extending Caching to Handle Query Parameters and Request Bodies
For more dynamic API requests, extend the caching service to handle query parameters and request bodies.
Generating Unique Cache Keys
Create a method to generate unique cache keys:
private generateCacheKey(url: string, params: any, body: any): string { return `${url}|${JSON.stringify(params)}|${JSON.stringify(body)}`; }
1private generateCacheKey(url: string, params: any, body: any): string {
2 return `${url}|${JSON.stringify(params)}|${JSON.stringify(body)}`;
3}
Updating Get and Set Methods
Update the get
and set
methods to use the unique cache keys:
get(url: string, params: any = {}, body: any = {}): Observable<any> | null { const key = this.generateCacheKey(url, params, body); return this.cache.has(key) ? of(this.cache.get(key)) : null; } set(url: string, params: any = {}, body: any = {}, value: any): void { const key = this.generateCacheKey(url, params, body); this.cache.set(key, value); }
1get(url: string, params: any = {}, body: any = {}): Observable<any> | null {
2 const key = this.generateCacheKey(url, params, body);
3 return this.cache.has(key) ? of(this.cache.get(key)) : null;
4}
5set(url: string, params: any = {}, body: any = {}, value: any): void {
6 const key = this.generateCacheKey(url, params, body);
7 this.cache.set(key, value);
8}
9
Implementing Caching with HTTP Interceptors
For a more global caching approach, use HTTP interceptors to cache all HTTP requests.
Creating a Cache Interceptor
Generate a cache interceptor:
ng generate interceptor cache
1ng generate interceptor cache
Implement the caching logic in the interceptor:
@Injectable() export class CacheInterceptor implements HttpInterceptor { private cache = new Map<string, any>(); intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { if (req.method !== 'GET') { return next.handle(req); } const cachedResponse = this.cache.get(req.urlWithParams); if (cachedResponse) { return of(cachedResponse); } return next.handle(req).pipe( tap(event => { if (event instanceof HttpResponse) { this.cache.set(req.urlWithParams, event); } }) ); } }
1@Injectable()
2export class CacheInterceptor implements HttpInterceptor {
3 private cache = new Map<string, any>();
4 intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
5 if (req.method !== 'GET') {
6 return next.handle(req);
7 }
8 const cachedResponse = this.cache.get(req.urlWithParams);
9 if (cachedResponse) {
10 return of(cachedResponse);
11 }
12 return next.handle(req).pipe(
13 tap(event => {
14 if (event instanceof HttpResponse) {
15 this.cache.set(req.urlWithParams, event);
16 }
17 })
18 );
19 }
20}
21
Registering the Interceptor
Register the interceptor globally in your Angular application:
providers: [ { provide: HTTP_INTERCEPTORS, useClass: CacheInterceptor, multi: true } ]
1providers: [
2 { provide: HTTP_INTERCEPTORS, useClass: CacheInterceptor, multi: true }
3]
Using Local Storage for Persistent Caching
To preserve the cache across page reloads, use local storage.
Storing and Retrieving Cache from Local Storage
Modify the caching service to use local storage:
set(key: string, value: any): void { this.cache.set(key, value); localStorage.setItem('cache', JSON.stringify(Array.from(this.cache.entries()))); } get(key: string): Observable<any> | null { if (this.cache.has(key)) { return of(this.cache.get(key)); } const storedCache = JSON.parse(localStorage.getItem('cache') || '[]'); this.cache = new Map(storedCache); return this.cache.has(key) ? of(this.cache.get(key)) : null; }
1set(key: string, value: any): void {
2 this.cache.set(key, value);
3 localStorage.setItem('cache', JSON.stringify(Array.from(this.cache.entries())));
4}
5get(key: string): Observable<any> | null {
6 if (this.cache.has(key)) {
7 return of(this.cache.get(key));
8 }
9 const storedCache = JSON.parse(localStorage.getItem('cache') || '[]');
10 this.cache = new Map(storedCache);
11 return this.cache.has(key) ? of(this.cache.get(key)) : null;
12}
13
Conclusion
Caching is an essential technique to optimize the performance of your Angular applications. By leveraging services, observables, interceptors, and local storage, you can efficiently manage and retrieve cached data. Implementing these methods will not only enhance your application's speed but also reduce unnecessary HTTP requests.
We hope you found this guide valuable and practical. If you enjoyed this post, please like, share, and subscribe to stay updated with the latest content.