Skip to content

Commit 4ecf93f

Browse files
committed
fix(pinia-orm): $isDirty() in hooks on model had wrong state
1 parent 26cab8e commit 4ecf93f

File tree

7 files changed

+82
-20
lines changed

7 files changed

+82
-20
lines changed

docs/content/1.guide/2.model/5.lifecycle-hooks.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class User extends Model {
4545
Mutation lifecycle hooks are called when you mutate data in the store. The corresponding hook name are `creating`, `updating`, `saving`, `deleting`, `created`, `updated`, `deleted` and `saved`.
4646

4747
When in `creating`, `updating` or `saving`, you can modify the record directly to mutate the data to be saved. You also have access
48-
to the original data. (Also available in `created`, `updated` and `saved`)
48+
to the data passed to the model. (Also available in `created`, `updated` and `saved`)
4949

5050
```js
5151
class Post extends Model {

packages/pinia-orm/.eslintcache

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

packages/pinia-orm/src/model/Model.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -799,7 +799,7 @@ export class Model {
799799
return this
800800
}
801801

802-
protected $fillMeta (action = 'save') {
802+
public $fillMeta (action = 'save') {
803803
const timestamp = Math.floor(Date.now() / 1000)
804804
if (action === 'save') {
805805
// @ts-expect-error Setting an object

packages/pinia-orm/src/query/Query.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -837,17 +837,17 @@ export class Query<M extends Model = Model> {
837837
const record = elements[id]
838838
const existing = currentData[id]
839839
let model = existing
840-
? this.hydrate({ ...existing, ...record }, { operation: 'set', action: 'update' })
840+
? Object.assign(this.hydrate(existing, { operation: 'set', action: 'update' }), record)
841841
: this.hydrate(record, { operation: 'set', action: 'save' })
842842

843-
const isSaving = model.$self().saving(model, existing ?? {})
844-
const isUpdatingOrCreating = existing ? model.$self().updating(model, existing ?? {}) : model.$self().creating(model, existing ?? {})
843+
const isSaving = model.$self().saving(model, record)
844+
const isUpdatingOrCreating = existing ? model.$self().updating(model, record) : model.$self().creating(model, record)
845845
if (isSaving === false || isUpdatingOrCreating === false) { continue }
846846

847847
if (model.$isDirty()) { model = this.hydrate(model.$getAttributes(), { operation: 'set', action: existing ? 'update' : 'save' }) }
848848

849-
afterSavingHooks.push(() => model.$self().saved(model, existing ?? {}))
850-
afterSavingHooks.push(() => existing ? model.$self().updated(model, existing ?? {}) : model.$self().created(model, existing ?? {}))
849+
afterSavingHooks.push(() => model.$self().saved(model, record))
850+
afterSavingHooks.push(() => existing ? model.$self().updated(model, record) : model.$self().created(model, record))
851851
newData[id] = model.$getAttributes()
852852
if (Object.values(model.$types()).length > 0 && !newData[id][model.$typeKey()]) { newData[id][model.$typeKey()] = record[model.$typeKey()] }
853853
}
@@ -893,9 +893,11 @@ export class Query<M extends Model = Model> {
893893
if (isEmpty(models)) { return [] }
894894

895895
const newModels = models.map((model) => {
896-
const newModel = this.hydrate({ ...model.$getAttributes(), ...record }, { action: 'update', operation: 'set' })
897-
if (model.$self().updating(model, record) === false) { return model }
898-
newModel.$self().updated(newModel)
896+
const oldModelUpdate = Object.assign(this.hydrate(model.$getAttributes(), { action: 'update', operation: 'set' }), record)
897+
if (model.$self().updating(oldModelUpdate, record) === false) { return model }
898+
899+
const newModel = oldModelUpdate.$isDirty() ? this.hydrate({ ...model.$getAttributes(), ...record }, { action: 'update', operation: 'set' }) : oldModelUpdate
900+
newModel.$self().updated(newModel, record)
899901
return newModel
900902
})
901903

packages/pinia-orm/tests/feature/hooks/saving.spec.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { describe, expect, it, vi } from 'vitest'
22

3-
import type { Element } from '../../../src'
43
import { BelongsTo as BelongsToClass, Model, useRepo } from '../../../src'
54
import { Attr, BelongsTo, Num, Str, Uid } from '../../../src/decorators'
65
import { assertState, mockUid } from '../../helpers'
@@ -38,11 +37,10 @@ describe('feature/hooks/saving', () => {
3837
@Num(0) declare age: number
3938
@BelongsTo(() => Post, 'postId') declare post: User | null
4039

41-
static saving (model: Model, record?: Element) {
42-
expect(model.name).not.toBe(record.name)
40+
static saving (model: User) {
4341
const fields = model.$fields()
4442
for (const name in fields) {
45-
if (fields[name] instanceof BelongsToClass && record && record.name === 'John Doe') { model[(fields[name] as BelongsToClass).foreignKey] = null }
43+
if (fields[name] instanceof BelongsToClass && model.name === 'John') { model[(fields[name] as BelongsToClass).foreignKey] = null }
4644
}
4745
}
4846
}

packages/pinia-orm/tests/feature/hooks/updating.spec.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ describe('feature/hooks/updating', () => {
4949
@Num(0) age!: number
5050

5151
static updating (model: Model) {
52+
expect(model.$isDirty()).toBe(true)
5253
model.name = 'John'
5354
}
5455
}
@@ -83,7 +84,7 @@ describe('feature/hooks/updating', () => {
8384
})
8485
})
8586

86-
it('is stopping record to be saved', () => {
87+
it('is not dirty if same value is set', () => {
8788
class User extends Model {
8889
static entity = 'users'
8990

@@ -92,7 +93,40 @@ describe('feature/hooks/updating', () => {
9293
@Num(0) age!: number
9394

9495
static updating (model: Model) {
96+
expect(model.$isDirty()).toBe(false)
9597
model.name = 'John'
98+
}
99+
}
100+
101+
const updatingMethod = vi.spyOn(User, 'updating')
102+
103+
fillState({
104+
users: {
105+
1: { id: 1, name: 'John Doe', age: 10 },
106+
2: { id: 2, name: 'John Doe', age: 10 },
107+
},
108+
})
109+
useRepo(User).where('id', 1).update({ age: 10 })
110+
111+
expect(updatingMethod).toHaveBeenCalledTimes(1)
112+
113+
assertState({
114+
users: {
115+
1: { id: 1, name: 'John Doe', age: 10 },
116+
2: { id: 2, name: 'John Doe', age: 10 },
117+
},
118+
})
119+
})
120+
121+
it('is stopping record to be saved', () => {
122+
class User extends Model {
123+
static entity = 'users'
124+
125+
@Num(0) id!: number
126+
@Str('') name!: string
127+
@Num(0) age!: number
128+
129+
static updating () {
96130
return false
97131
}
98132
}
@@ -109,6 +143,10 @@ describe('feature/hooks/updating', () => {
109143

110144
expect(updatingMethod).toHaveBeenCalledOnce()
111145

146+
useRepo(User).where('id', 1).update({ name: 'John Doe', age: 30 })
147+
148+
expect(updatingMethod).toHaveBeenCalled(2)
149+
112150
assertState({
113151
users: {
114152
1: { id: 1, name: 'John', age: 50 },

packages/pinia-orm/tests/performance/prevent_rerender_of_child_components.spec.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,32 @@ describe('performance/prevent_rerender_of_child_components', () => {
4646
})
4747
}
4848

49+
const updatePost = () => {
50+
postRepo.where('id', 1).update({
51+
id: 1,
52+
title: `Test 1001`,
53+
})
54+
}
55+
56+
const savePost = () => {
57+
postRepo.save({
58+
id: 1,
59+
title: `Test 1111`,
60+
})
61+
}
62+
4963
return {
5064
posts,
5165
addPost,
66+
updatePost,
67+
savePost,
5268
}
5369
},
5470
template: `
5571
<div>
56-
<button @click="addPost" > Click me </button>
72+
<button id='insert' @click="addPost" > Click me </button>
73+
<button id='update' @click="updatePost" > Update 1 </button>
74+
<button id='save' @click="savePost" > Update 2 </button>
5775
<post-component v-for="post in posts" :post="post" :key="post.id" />
5876
</div>`,
5977
})
@@ -80,11 +98,11 @@ describe('performance/prevent_rerender_of_child_components', () => {
8098

8199
const logger = vi.spyOn(console, 'warn')
82100

83-
await wrapper.find('button').trigger('click')
101+
await wrapper.find('button#insert').trigger('click')
84102
await nextTick()
85-
await wrapper.find('button').trigger('click')
103+
await wrapper.find('button#insert').trigger('click')
86104
await nextTick()
87-
await wrapper.find('button').trigger('click')
105+
await wrapper.find('button#insert').trigger('click')
88106
await nextTick()
89107

90108
expect(wrapper.html()).toContain('Test 1')
@@ -93,5 +111,11 @@ describe('performance/prevent_rerender_of_child_components', () => {
93111
expect(wrapper.html()).toContain('Test 13')
94112

95113
expect(logger).not.toBeCalled()
114+
115+
await wrapper.find('button#update').trigger('click')
116+
await nextTick()
117+
118+
await wrapper.find('button#save').trigger('click')
119+
await nextTick()
96120
})
97121
})

0 commit comments

Comments
 (0)