@delon/cache Caching Strategies Skill
This skill helps implement caching using @delon/cache library.
Core Principles
Cache Types
- Memory Cache: Fast, in-memory caching (lost on page refresh)
- LocalStorage Cache: Persistent across sessions
- SessionStorage Cache: Persistent within session only
Features
- TTL-based expiration
- Cache invalidation (manual and automatic)
- Cache grouping and namespacing
- HTTP request caching with interceptors
- Observable support for async data
Configuration
// src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideDelonCache } from '@delon/cache';
export const appConfig: ApplicationConfig = {
providers: [
provideDelonCache({
mode: 'promise', // 'promise' | 'none'
request_method: 'POST',
meta_key: '__cache_meta',
prefix: '',
expire: 3600000 // Default TTL: 1 hour (ms)
})
]
};
Cache Service
// src/app/core/services/cache.service.ts
import { Injectable, inject } from '@angular/core';
import { CacheService as DelonCacheService } from '@delon/cache';
import { Observable } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class CacheService {
private cache = inject(DelonCacheService);
/**
* Set cache with key
*/
set<T>(key: string, value: T, options?: {
type?: 'memory' | 'localStorage' | 'sessionStorage';
expire?: number; // TTL in milliseconds
}): void {
this.cache.set(key, value, {
type: options?.type || 'memory',
expire: options?.expire || 3600000 // 1 hour default
});
}
/**
* Get cache by key
*/
get<T>(key: string): T | null {
return this.cache.get<T>(key);
}
/**
* Check if cache exists and is not expired
*/
has(key: string): boolean {
return this.cache.has(key);
}
/**
* Remove cache by key
*/
remove(key: string): void {
this.cache.remove(key);
}
/**
* Clear all cache
*/
clear(): void {
this.cache.clear();
}
/**
* Get or set cache (lazy loading pattern)
*/
getOrSet<T>(
key: string,
factory: () => Observable<T> | Promise<T>,
options?: {
type?: 'memory' | 'localStorage' | 'sessionStorage';
expire?: number;
}
): Observable<T> {
if (this.has(key)) {
return new Observable(observer => {
observer.next(this.get<T>(key)!);
observer.complete();
});
}
const result = factory();
if (result instanceof Observable) {
return new Observable(observer => {
result.subscribe({
next: (value) => {
this.set(key, value, options);
observer.next(value);
},
error: (err) => observer.error(err),
complete: () => observer.complete()
});
});
}
return new Observable(observer => {
result.then(value => {
this.set(key, value, options);
observer.next(value);
observer.complete();
}).catch(err => observer.error(err));
});
}
}
Memory Cache (Default)
import { Component, inject, signal } from '@angular/core';
import { CacheService } from '@core/services/cache.service';
@Component({
selector: 'app-task-list',
template: `
<button nz-button (click)="loadTasks()">Load Tasks</button>
<button nz-button (click)="clearCache()">Clear Cache</button>
@if (loading()) {
<nz-spin />
} @else {
@for (task of tasks(); track task.id) {
<div>{{ task.title }}</div>
}
}
`
})
export class TaskListComponent {
private cacheService = inject(CacheService);
private taskService = inject(TaskService);
loading = signal(false);
tasks = signal<Task[]>([]);
private readonly CACHE_KEY = 'tasks:list';
async loadTasks(): Promise<void> {
// Try to get from cache first
const cached = this.cacheService.get<Task[]>(this.CACHE_KEY);
if (cached) {
console.log('Loading from cache');
this.tasks.set(cached);
return;
}
// Load from API
this.loading.set(true);
try {
const tasks = await this.taskService.getTasks();
// Cache for 5 minutes
this.cacheService.set(this.CACHE_KEY, tasks, {
type: 'memory',
expire: 5 * 60 * 1000 // 5 minutes
});
this.tasks.set(tasks);
} finally {
this.loading.set(false);
}
}
clearCache(): void {
this.cacheService.remove(this.CACHE_KEY);
console.log('Cache cleared');
}
}
LocalStorage Cache (Persistent)
import { Injectable, inject } from '@angular/core';
import { CacheService } from '@core/services/cache.service';
@Injectable({ providedIn: 'root' })
export class UserPreferencesService {
private cacheService = inject(CacheService);
private readonly CACHE_KEY = 'user:preferences';
/**
* Save user preferences (persists across sessions)
*/
savePreferences(preferences: UserPreferences): void {
this.cacheService.set(this.CACHE_KEY, preferences, {
type: 'localStorage',
expire: 30 * 24 * 60 * 60 * 1000 // 30 days
});
}
/**
* Load user preferences
*/
loadPreferences(): UserPreferences | null {
return this.cacheService.get<UserPreferences>(this.CACHE_KEY);
}
/**
* Clear preferences
*/
clearPreferences(): void {
this.cacheService.remove(this.CACHE_KEY);
}
}
interface UserPreferences {
theme: 'light' | 'dark';
language: string;
sidebarCollapsed: boolean;
}
SessionStorage Cache
/**
* Cache search results for current session only
*/
@Injectable({ providedIn: 'root' })
export class SearchService {
private cacheService = inject(CacheService);
async search(query: string): Promise<SearchResult[]> {
const cacheKey = `search:${query}`;
// Check session cache
const cached = this.cacheService.get<SearchResult[]>(cacheKey);
if (cached) {
return cached;
}
// Perform search
const results = await this.performSearch(query);
// Cache for current session only
this.cacheService.set(cacheKey, results, {
type: 'sessionStorage',
expire: 30 * 60 * 1000 // 30 minutes
});
return results;
}
private async performSearch(query: string): Promise<SearchResult[]> {
// API call
return [];
}
}
Lazy Loading with getOrSet
@Injectable({ providedIn: 'root' })
export class ConfigService {
private cacheService = inject(CacheService);
private http = inject(HttpClient);
/**
* Load configuration with automatic caching
*/
loadConfig(): Observable<AppConfig> {
return this.cacheService.getOrSet(
'app:config',
() => this.http.get<AppConfig>('/api/config'),
{
type: 'localStorage',
expire: 24 * 60 * 60 * 1000 // 24 hours
}
);
}
}
Cache Invalidation
Manual Invalidation
@Injectable({ providedIn: 'root' })
export class TaskService {
private cacheService = inject(CacheService);
private taskRepository = inject(TaskRepository);
/**
* Create task and invalidate cache
*/
async createTask(task: Omit<Task, 'id'>): Promise<Task> {
const created = await this.taskRepository.create(task);
// Invalidate task list cache
this.cacheService.remove('tasks:list');
this.cacheService.remove(`tasks:blueprint:${task.blueprintId}`);
return created;
}
/**
* Update task and invalidate cache
*/
async updateTask(id: string, updates: Partial<Task>): Promise<Task> {
const updated = await this.taskRepository.update(id, updates);
// Invalidate specific task cache
this.cacheService.remove(`tasks:${id}`);
// Invalidate list caches
this.cacheService.remove('tasks:list');
return updated;
}
}
Group Cache Invalidation
@Injectable({ providedIn: