import { Injector } from '@angular/core'; import { DefaultError } from '@tanstack/query-core'; import { CreateMutationOptions, CreateMutationResult } from './types.js'; export interface InjectMutationOptions { /** * The `Injector` in which to create the mutation. * * If this is not provided, the current injection context will be used instead (via `inject`). */ injector?: Injector; } /** * Unlike queries, mutations are typically used to create/update/delete data or perform server side-effects. * `injectMutation` is the function for that. Unlike queries, mutations are not run automatically. * * @remarks `mutate`/`mutateAsync` also accept per-call `onSuccess`/`onError`/`onSettled` callbacks as a * second argument, useful for triggering call-site side effects (e.g. navigation) without coupling them to * the shared mutation definition. Callbacks defined in `injectMutationFn` fire for every mutation; per-call * callbacks fire only for the latest call you've made — `mutateAsync` gives you a promise per call instead, * so you can await `Promise.all`/`Promise.allSettled` over several calls and see each one's outcome. * @see {@link mutationOptions} to share these options across multiple `injectMutation` call sites, or to look * the mutation up elsewhere via its `mutationKey` (e.g. with `injectMutationState`). * @param injectMutationFn - A function that returns mutation options. Similar to `computed` from Angular, * this function runs in the reactive context, so signals read inside it drive the mutation's options. * @param options - Additional configuration * @returns The mutation result. Value fields are exposed as a `Signal` — read `data`/`error` by calling them * (e.g. `mutation.data()`) — while function fields (`mutate`, `mutateAsync`, `reset`) are called directly, * unchanged. `isSuccess`/`isError`/`isPending`/`isIdle` are type-guard methods you can call to narrow whether * `data` is defined. * * @example * ```angular-ts * @Component({ * selector: 'todos', * template: ` * @if (addMutation.isPending()) { * Adding todo... * } @else if (addMutation.isError()) { *
An error occurred: {{ addMutation.error()?.message }}
* } * * `, * }) * export class Todos { * readonly #queryClient = inject(QueryClient) * * readonly addMutation = injectMutation(() => ({ * mutationFn: addTodo, * onSuccess: () => this.#queryClient.invalidateQueries({ queryKey: ['todos'] }), * })) * } * ``` * * @example * Optimistic update via `onMutate`, rolling back on `onError`: * ```angular-ts * @Component({ * selector: 'todos', * template: ``, * }) * export class Todos { * readonly #queryClient = inject(QueryClient) * * readonly addMutation = injectMutation(() => ({ * mutationFn: addTodo, * onMutate: async (newTodo) => { * await this.#queryClient.cancelQueries({ queryKey: ['todos'] }) * const previousTodos = this.#queryClient.getQueryData>(['todos']) * * this.#queryClient.setQueryData>(['todos'], (old) => [ * ...(old ?? []), * newTodo, * ]) * * // Passed to `onError` as `onMutateResult` if the mutation fails. * return { previousTodos } * }, * onError: (_err, _newTodo, onMutateResult) => { * this.#queryClient.setQueryData(['todos'], onMutateResult?.previousTodos) * }, * onSettled: () => { * this.#queryClient.invalidateQueries({ queryKey: ['todos'] }) * }, * })) * } * ``` * * @example * Callbacks passed per call to `mutate` only fire for the last call — `mutateAsync` gives you a promise per * call instead, so you can wait for all of them when they succeed: * ```angular-ts * @Component({ * selector: 'todos', * template: ` * * `, * }) * export class Todos { * readonly #queryClient = inject(QueryClient) * * readonly addMutation = injectMutation(() => ({ * mutationFn: addTodo, * onSuccess: () => this.#queryClient.invalidateQueries({ queryKey: ['todos'] }), * })) * * async handleAddAll(todos: Array) { * try { * await Promise.all(todos.map((todo) => this.addMutation.mutateAsync(todo))) * } catch (error) { * console.error('Failed to add todos:', error) * } * } * } * ``` * * @example * If some of the mutations above can fail independently of the others, and you want to know which ones did — * rather than losing that information the moment the first one rejects — swap `Promise.all` for * `Promise.allSettled`: * ```angular-ts * @Component({ * selector: 'todos', * template: ` * * `, * }) * export class Todos { * readonly #queryClient = inject(QueryClient) * * readonly addMutation = injectMutation(() => ({ * mutationFn: addTodo, * onSuccess: () => this.#queryClient.invalidateQueries({ queryKey: ['todos'] }), * })) * * async handleAddAll(todos: Array) { * const addResults = await Promise.allSettled( * todos.map((todo) => this.addMutation.mutateAsync(todo)), * ) * * addResults.forEach((addResult, index) => { * if (addResult.status === 'rejected') { * console.error(`Failed to add "${todos[index]}":`, addResult.reason) * } * }) * } * } * ``` */ export declare function injectMutation(injectMutationFn: () => CreateMutationOptions, options?: InjectMutationOptions): CreateMutationResult;