Skip to content

Quick start

One provider, two durations.

Terminal window
npm install ngx-ziflux
app.config.ts
import { provideZiflux } from 'ngx-ziflux'
export const appConfig: ApplicationConfig = {
providers: [
provideZiflux({
staleTime: 30_000, // 30s, data considered fresh
expireTime: 300_000, // 5min, stale data evicted
}),
],
}

Add a DataCache instance to your existing API service. One line.

order.api.ts
import { inject, Injectable } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { DataCache } from 'ngx-ziflux'
@Injectable({ providedIn: 'root' })
export class OrderApi {
readonly cache = new DataCache() // ← this is new
readonly #http = inject(HttpClient)
getAll$(filters: OrderFilters) {
return this.#http.get<Order[]>('/orders', { params: { ...filters } })
}
}

Same shape as resource(), plus cache and cacheKey. It returns stale data immediately and re-fetches in the background.

order-list.store.ts
import { cachedResource } from 'ngx-ziflux'
@Injectable()
export class OrderListStore {
readonly #api = inject(OrderApi)
readonly filters = signal<OrderFilters>({ status: 'all' })
readonly orders = cachedResource({
cache: this.#api.cache,
cacheKey: params => ['order', 'list', params.status],
params: () => this.filters(),
loader: ({ params }) => this.#api.getAll$(params),
})
}

isInitialLoading() is true only when there is no cached data, so a return visit skips the spinner as long as the entry has not passed expireTime.

order-list.component.ts
@Component({
providers: [OrderListStore],
template: `
@if (store.orders.isInitialLoading()) {
<app-spinner />
} @else {
<app-order-list [orders]="store.orders.value()" />
}
`,
})
export class OrderListComponent {
readonly store = inject(OrderListStore)
}

Navigate away, come back, and the data loads immediately from cache.

For read-only use cases, you can skip the Store layer entirely. See the factory pattern.