@void-snippets/angular

Optimistic Updates

Cache transforms with rollback, effectiveBase stacking, and lifecycle hooks — identical semantics to React.

#What it does

Works identically to the React version — the same WeakMap-based stack, the same rollback-and-replay semantics. Configure the handlers once in createResourceInject and every mutation from injectMutations picks them up automatically.

#Configuration

typescript
export function useContactResource() {
  const service = inject(ContactsService);

  return createResourceInject('contacts', service, {
    optimistic: {
      // Applied immediately when update.mutate() fires — before the API responds
      update: (cache, { _id, payload }) =>
        cache.map((c) => (c._id === _id ? { ...c, ...payload } : c)),

      // Item disappears instantly — restored automatically on API error
      remove: (cache, id) =>
        cache.filter((c) => c._id !== id),

      // New item appears at top with a temporary ID
      create: (cache, { payload, tempId }) => [
        { ...payload, _id: tempId as Contact.Id },
        ...cache,
      ],

      // Only fires after rollback is complete — cache is already correct
      onError: (err, op) => {
        console.error(`Optimistic ${op.kind} failed, rolled back:`, err.message);
      },

      onSuccess: (op) => {
        if (op.kind === 'create') analytics.track('contact_created');
      },
    },
  });
}

#Handler reference

HandlerFires onSignature
createcreate.mutate()(cache, { payload, tempId }) => TBase[]
updateupdate.mutate()(cache, { _id, payload }) => TBase[]
updateSingleupdate.mutate() (overrides the default shallow-merge for injectGet)(current, payload) => TDetail
removeremove.mutate()(cache, id) => TBase[]
onErrorAny mutation failure, after rollback completes(error, operation) => void
onSuccessAny mutation success, after effectiveBase advances(operation) => void

#Concurrent rollback

The library maintains an ordered stack of every in-flight mutation and a "before" snapshot of the cache. If you delete item A, rename item B, and create item C concurrently, and B's rename fails:

  1. B is removed from the pending stack
  1. The cache is restored to the original snapshot
  1. A's delete and C's create are replayed in order

None of A's or C's changes are lost.