@void-snippets/angular

injectMutations

create, update, and remove mutations with Signal-based status fields.

#What it does

Returns three mutation objects — create, update, and remove. When any mutation completes, TanStack Angular Query invalidates the cache and triggers a background refetch for all mounted list/get queries for this resource.

#Signature

typescript
contactResource.injectMutations(): VSAngularMutationsResult<Contact.Id, Contact.Detail, Contact.Apis.Create, Contact.Apis.Update>

#Each mutation object

VSAngularMutationState:

FieldTypeDescription
mutate(vars)(vars: TVars) => voidFire and forget. Great for delete buttons.
mutateAsync(vars)(vars: TVars) => PromiseUse with await for sequential control — close a modal only after success.
isPendingSignaltrue while in flight. Wire to a button's disabled and spinner.
isSuccessSignaltrue after the last call succeeded.
isErrorSignaltrue after the last call failed.
errorSignalThe error from the last failed call.
dataSignalServer response from the last successful call.
reset()() => voidResets isSuccess, isError, and error to idle.

#The key pattern — await before closing a modal

typescript
@Component({
  template: `
    <button (click)="openCreate()">+ New Contact</button>

    @for (contact of contacts.list(); track contact._id) {
      <app-contact-row
        [contact]="contact"
        (delete)="mutations.remove.mutate(contact._id)"
      />
    }

    <app-contact-dialog
      [open]="dialogOpen()"
      [saving]="mutations.create.isPending() || mutations.update.isPending()"
      (save)="onSave($event)"
    />
  `,
})
export class ContactsComponent {
  private contactResource = useContactResource();
  contacts  = this.contactResource.injectList();
  mutations = this.contactResource.injectMutations();

  dialogOpen = signal(false);
  dialogData = signal<Contact.Base | null>(null);

  async onSave(formData: Contact.Apis.Create | Contact.Apis.Update) {
    try {
      const existing = this.dialogData();
      if (existing) {
        await this.mutations.update.mutateAsync({ _id: existing._id, payload: formData as Contact.Apis.Update });
      } else {
        await this.mutations.create.mutateAsync(formData as Contact.Apis.Create);
      }
      // Only runs on success — modal closes, list refreshes automatically
      this.dialogOpen.set(false);
    } catch (err) {
      // Modal stays open so the user can fix and retry
      console.error(err);
    }
  }
}