Mutations & Optimistic Updates
This tutorial demonstrates how to handle data mutations (creating/updating) and keeping the UI in sync using mutate.
Scenario
We have a user profile form. When we save, we want to update the UI immediately (optimistic update) or after the API confirms success.
The Component
import { Component, inject } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { cachedResource } from 'ngx-cachr';
@Component({
selector: 'app-user-profile',
standalone: true,
imports: [FormsModule],
template: `
<h3>User Profile</h3>
@if (user.data(); as u) {
<div class="form-group">
<label>Name:</label>
<input [(ngModel)]="editName" placeholder="Enter name">
</div>
<button (click)="save(u)" [disabled]="isSaving">
{{ isSaving ? 'Saving...' : 'Save Changes' }}
</button>
<p>Current Cache: {{ u.name }}</p>
}
`
})
export class UserProfileComponent {
editName = '';
isSaving = false;
// Fetch user data
user = cachedResource({
key: 'user-profile',
loader: async () => {
return { id: 1, name: 'John Doe' };
}
});
constructor() {
// Initialize form when data loads
effect(() => {
const u = this.user.data();
if (u) this.editName = u.name;
});
}
async save(currentUser: any) {
this.isSaving = true;
const newName = this.editName;
try {
// 1. Optimistic Update (Optional)
// Update the cache immediately before the API call finishes
this.user.mutate({ ...currentUser, name: newName });
// 2. API Call
await this.updateUserApi(currentUser.id, newName);
// 3. Verification (Optional)
// If the API returns the updated object, we can mutate again with the "real" data
// or simply invalidate to re-fetch fresh data.
// this.user.invalidate();
} catch (err) {
// Revert on error
console.error('Save failed', err);
this.user.mutate(currentUser); // Rollback
alert('Failed to save!');
} finally {
this.isSaving = false;
}
}
// Mock API call
private async updateUserApi(id: number, name: string) {
return new Promise(resolve => setTimeout(resolve, 1000));
}
}
Key Concepts
mutate(newData): Directly updates the signaldataand the underlying cache (Memory & Storage). This is perfect for instant UI feedback.- Error Handling: If the API call fails, you can simply call
mutateagain with the old data to rollback the changes. - Persistence: Since
mutateupdates the storage driver, if the user refreshes the page immediately after an optimistic update, they will still see the new data.