diff --git a/packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts b/packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts
index bf3c37f39b..9e72b5e245 100644
--- a/packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts
+++ b/packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts
@@ -144,7 +144,6 @@ describe('injectMutationState', () => {
{{ mutation.status }}
}
`,
- standalone: true,
})
class FakeComponent {
name = input.required()
diff --git a/packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts b/packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts
index 3ea18743d7..2ecccf882e 100644
--- a/packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts
+++ b/packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts
@@ -58,8 +58,6 @@ describe('injectMutation', () => {
}))
})
- TestBed.tick()
-
mutation.mutate(result)
vi.advanceTimersByTime(1)
@@ -133,9 +131,17 @@ describe('injectMutation', () => {
mutation.mutate('xyz')
- const mutations = mutationCache.find({ mutationKey: ['2'] })
+ mutationKey.set(['3'])
+
+ mutation.mutate('xyz')
- expect(mutations?.options.mutationKey).toEqual(['2'])
+ expect(mutationCache.find({ mutationKey: ['1'] })).toBeUndefined()
+ expect(
+ mutationCache.find({ mutationKey: ['2'] })?.options.mutationKey,
+ ).toEqual(['2'])
+ expect(
+ mutationCache.find({ mutationKey: ['3'] })?.options.mutationKey,
+ ).toEqual(['3'])
})
test('should reset state after invoking mutation.reset', async () => {
@@ -310,7 +316,6 @@ describe('injectMutation', () => {
{{ mutation.data() }}
`,
- standalone: true,
})
class FakeComponent {
name = input.required()
@@ -333,8 +338,6 @@ describe('injectMutation', () => {
button.triggerEventHandler('click')
await resolveMutations()
- fixture.detectChanges()
-
const text = debugElement.query(By.css('span')).nativeElement.textContent
expect(text).toEqual('value')
const mutation = mutationCache.find({ mutationKey: ['fake', 'value'] })
@@ -351,7 +354,6 @@ describe('injectMutation', () => {
{{ mutation.data() }}
`,
- standalone: true,
})
class FakeComponent {
name = input.required()
@@ -394,6 +396,37 @@ describe('injectMutation', () => {
expect(mutation2!.options.mutationKey).toEqual(['fake', 'updatedValue'])
})
+ test('should have pending state when mutating in constructor', async () => {
+ @Component({
+ selector: 'app-fake',
+ template: `
+ {{ mutation.isPending() ? 'pending' : 'not pending' }}
+ `,
+ })
+ class FakeComponent {
+ mutation = injectMutation(() => ({
+ mutationKey: ['fake'],
+ mutationFn: () => sleep(0).then(() => 'fake'),
+ }))
+
+ constructor() {
+ this.mutation.mutate()
+ }
+ }
+
+ const fixture = TestBed.createComponent(FakeComponent)
+ const { debugElement } = fixture
+ const span = debugElement.query(By.css('span'))
+
+ vi.advanceTimersByTime(1)
+
+ expect(span.nativeElement.textContent).toEqual('pending')
+
+ await resolveMutations()
+
+ expect(span.nativeElement.textContent).toEqual('not pending')
+ })
+
describe('throwOnError', () => {
test('should evaluate throwOnError when mutation is expected to throw', async () => {
const err = new Error('Expected mock error. All is well!')
diff --git a/packages/angular-query-experimental/src/__tests__/inject-query.test.ts b/packages/angular-query-experimental/src/__tests__/inject-query.test.ts
index 44964b7927..0309296430 100644
--- a/packages/angular-query-experimental/src/__tests__/inject-query.test.ts
+++ b/packages/angular-query-experimental/src/__tests__/inject-query.test.ts
@@ -520,7 +520,6 @@ describe('injectQuery', () => {
@Component({
selector: 'app-fake',
template: `{{ query.data() }}`,
- standalone: true,
})
class FakeComponent {
name = input.required()
diff --git a/packages/angular-query-experimental/src/inject-mutation.ts b/packages/angular-query-experimental/src/inject-mutation.ts
index 16b22ed2f7..ad8fadc015 100644
--- a/packages/angular-query-experimental/src/inject-mutation.ts
+++ b/packages/angular-query-experimental/src/inject-mutation.ts
@@ -4,7 +4,6 @@ import {
NgZone,
assertInInjectionContext,
computed,
- effect,
inject,
signal,
untracked,
@@ -71,7 +70,15 @@ export function injectMutation<
null
return computed(() => {
- return (instance ||= new MutationObserver(queryClient, optionsSignal()))
+ const observerOptions = optionsSignal()
+ return untracked(() => {
+ if (instance) {
+ instance.setOptions(observerOptions)
+ } else {
+ instance = new MutationObserver(queryClient, observerOptions)
+ }
+ return instance
+ })
})
})()
@@ -84,14 +91,6 @@ export function injectMutation<
}
})
- /**
- * Computed signal that gets result from mutation cache based on passed options
- */
- const resultFromInitialOptionsSignal = computed(() => {
- const observer = observerSignal()
- return observer.getCurrentResult()
- })
-
/**
* Signal that contains result set by subscriber
*/
@@ -102,50 +101,36 @@ export function injectMutation<
TContext
> | null>(null)
- effect(
- () => {
- const observer = observerSignal()
- const observerOptions = optionsSignal()
-
- untracked(() => {
- observer.setOptions(observerOptions)
- })
- },
- {
- injector,
- },
- )
-
- effect(
- () => {
- // observer.trackResult is not used as this optimization is not needed for Angular
- const observer = observerSignal()
+ /**
+ * Computed signal that gets result from mutation cache based on passed options
+ */
+ const resultFromInitialOptionsSignal = computed(() => {
+ const observer = observerSignal()
- untracked(() => {
- const unsubscribe = ngZone.runOutsideAngular(() =>
- observer.subscribe(
- notifyManager.batchCalls((state) => {
- ngZone.run(() => {
- if (
- state.isError &&
- shouldThrowError(observer.options.throwOnError, [state.error])
- ) {
- ngZone.onError.emit(state.error)
- throw state.error
- }
+ untracked(() => {
+ const unsubscribe = ngZone.runOutsideAngular(() =>
+ // observer.trackResult is not used as this optimization is not needed for Angular
+ observer.subscribe(
+ notifyManager.batchCalls((state) => {
+ ngZone.run(() => {
+ if (
+ state.isError &&
+ shouldThrowError(observer.options.throwOnError, [state.error])
+ ) {
+ ngZone.onError.emit(state.error)
+ throw state.error
+ }
+
+ resultFromSubscriberSignal.set(state)
+ })
+ }),
+ ),
+ )
+ destroyRef.onDestroy(unsubscribe)
+ })
- resultFromSubscriberSignal.set(state)
- })
- }),
- ),
- )
- destroyRef.onDestroy(unsubscribe)
- })
- },
- {
- injector,
- },
- )
+ return observer.getCurrentResult()
+ })
const resultSignal = computed(() => {
const resultFromSubscriber = resultFromSubscriberSignal()