mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-12 12:32:25 +03:00
fix: resolving arrays (collections still wip)
This commit is contained in:
@@ -21,7 +21,16 @@ export function createHandler (context: MetaContext, pathSegments: PathSegments
|
||||
const keyPath: PathSegments = [...pathSegments, key]
|
||||
|
||||
const handler = /* #__PURE__ */ createHandler(context, keyPath)
|
||||
value.__vm_proxy = createProxy(value, handler)
|
||||
Object.defineProperty(
|
||||
value,
|
||||
'__vm_proxy',
|
||||
{
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
value: createProxy(value, handler)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return value.__vm_proxy
|
||||
@@ -31,17 +40,25 @@ export function createHandler (context: MetaContext, pathSegments: PathSegments
|
||||
key: string,
|
||||
value: any
|
||||
): boolean {
|
||||
update(context, pathSegments, key, value)
|
||||
// target[key] = value
|
||||
return true
|
||||
const success = Reflect.set(target, key, value)
|
||||
|
||||
if (success) {
|
||||
update(context, pathSegments, key, value)
|
||||
}
|
||||
|
||||
return success
|
||||
},
|
||||
deleteProperty (
|
||||
target: { [key: string]: any },
|
||||
prop: string
|
||||
) {
|
||||
remove(context, pathSegments, prop)
|
||||
delete target[prop]
|
||||
return true
|
||||
const success = Reflect.deleteProperty(target, prop)
|
||||
|
||||
if (success) {
|
||||
remove(context, pathSegments, prop)
|
||||
}
|
||||
|
||||
return success
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { hasOwn } from '@vue/shared'
|
||||
import { hasOwn, isArray } from '@vue/shared'
|
||||
import { clone } from '../utils'
|
||||
import { ActiveNode, GetActiveNode, MetaContext, PathSegments, ShadowNode, GetShadowNodes } from '../types'
|
||||
|
||||
@@ -11,12 +11,19 @@ export function resolveActive (
|
||||
) {
|
||||
let value
|
||||
|
||||
const shadowLength = shadowParent[key] ? shadowParent[key].length : 0
|
||||
const isUpdatingArrayKey = isArray(activeParent)
|
||||
|
||||
let shadowLength
|
||||
if (isUpdatingArrayKey) {
|
||||
shadowLength = shadowParent ? shadowParent.length : 0
|
||||
} else {
|
||||
shadowLength = shadowParent[key] ? shadowParent[key].length : 0
|
||||
}
|
||||
|
||||
if (shadowLength > 1) {
|
||||
// Is using freeze useful? Idea is to prevent the user from messing with these options by mistake
|
||||
const getShadow: GetShadowNodes = () => Object.freeze(clone(shadowParent[key]))
|
||||
const getActive: GetActiveNode = () => Object.freeze(clone(activeParent[key]))
|
||||
const getShadow: GetShadowNodes = () => Object.freeze(clone(isUpdatingArrayKey ? shadowParent : shadowParent[key]))
|
||||
const getActive: GetActiveNode = () => Object.freeze(clone(isUpdatingArrayKey ? activeParent : activeParent[key]))
|
||||
|
||||
value = context.resolve(
|
||||
key,
|
||||
@@ -30,6 +37,18 @@ export function resolveActive (
|
||||
|
||||
if (value === undefined) {
|
||||
delete activeParent[key]
|
||||
} else if (isUpdatingArrayKey) {
|
||||
// set new values
|
||||
for (const k in value) {
|
||||
activeParent[k] = value[k]
|
||||
}
|
||||
|
||||
// delete old values
|
||||
for (const k in activeParent) {
|
||||
if (!(k in value)) {
|
||||
delete activeParent[k]
|
||||
}
|
||||
}
|
||||
} else if (!hasOwn(activeParent, key) || activeParent[key] !== value) {
|
||||
activeParent[key] = value
|
||||
}
|
||||
|
||||
@@ -33,8 +33,6 @@ export function set (
|
||||
activeParent[key],
|
||||
pathSegments
|
||||
)
|
||||
} else if (isArray(value)) {
|
||||
|
||||
}
|
||||
|
||||
let idx = -1
|
||||
@@ -83,7 +81,6 @@ export function set (
|
||||
}
|
||||
|
||||
// Step 3: Update the active data
|
||||
|
||||
resolveActive(context, key, pathSegments, shadowParent, activeParent)
|
||||
}
|
||||
|
||||
|
||||
+110
-38
@@ -1,3 +1,4 @@
|
||||
import { isArray, isPlainObject } from '@vue/shared'
|
||||
import { createProxy, createHandler, setByObject, remove } from '../../src/continuous-object-merge'
|
||||
|
||||
describe('resolve', () => {
|
||||
@@ -8,9 +9,45 @@ describe('resolve', () => {
|
||||
active = {}
|
||||
shadow = {}
|
||||
|
||||
const resolve = (key, pathSegments, getOptions, getCurrentValue) => {
|
||||
const resolve = (_key, _pathSegments, getOptions, _getCurrentValue) => {
|
||||
const options = getOptions()
|
||||
console.log('RESOLVE', options)
|
||||
|
||||
const hasArrayOption = options.some(option => isArray(option.value))
|
||||
if (hasArrayOption) {
|
||||
const groupedOptions = {}
|
||||
for (const option of options) {
|
||||
console.log('OPTION', option)
|
||||
if (!isArray(option.value)) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (const value of option.value) {
|
||||
if (isPlainObject(value) && 'vmid' in value) {
|
||||
groupedOptions[value.vmid] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(groupedOptions)
|
||||
const values = []
|
||||
for (const option of options) {
|
||||
if (!isArray(option.value)) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (const value of option.value) {
|
||||
if (!isPlainObject(value) || !('vmid' in value)) {
|
||||
values.push(value)
|
||||
} else if (groupedOptions[value.vmid]) {
|
||||
values.push(groupedOptions[value.vmid])
|
||||
delete groupedOptions[value.vmid]
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log('VALUES', values)
|
||||
return values
|
||||
}
|
||||
|
||||
return options[options.length - 1].value
|
||||
}
|
||||
|
||||
@@ -32,7 +69,7 @@ describe('resolve', () => {
|
||||
|
||||
// Init proxy
|
||||
const handler1 = createHandler(context1)
|
||||
const proxy1 = createProxy(target1, handler1)
|
||||
/* const proxy1 = */ createProxy(target1, handler1)
|
||||
|
||||
setByObject(context2, target2)
|
||||
const handler2 = createHandler(context2)
|
||||
@@ -72,7 +109,7 @@ describe('resolve', () => {
|
||||
|
||||
// Init proxy
|
||||
const handler1 = createHandler(context1)
|
||||
const proxy1 = createProxy(target1, handler1)
|
||||
/* const proxy1 = */ createProxy(target1, handler1)
|
||||
|
||||
setByObject(context2, target2)
|
||||
const handler2 = createHandler(context2)
|
||||
@@ -102,7 +139,7 @@ describe('resolve', () => {
|
||||
expect(shadow.obj.key.length).toBe(0)
|
||||
})
|
||||
|
||||
test.skip('resolve (array)', () => {
|
||||
test('resolve (array)', () => {
|
||||
const target1 = {
|
||||
arr: [
|
||||
'array value 1'
|
||||
@@ -115,12 +152,8 @@ describe('resolve', () => {
|
||||
]
|
||||
}
|
||||
|
||||
// TODO: test this, is specifying a resolver enough or do we need to add an array-merge option
|
||||
|
||||
// Set initial value
|
||||
// Set initial value & init proxy
|
||||
setByObject(context1, target1)
|
||||
|
||||
// Init proxy
|
||||
const handler1 = createHandler(context1)
|
||||
const proxy1 = createProxy(target1, handler1)
|
||||
|
||||
@@ -128,47 +161,52 @@ describe('resolve', () => {
|
||||
const handler2 = createHandler(context2)
|
||||
const proxy2 = createProxy(target2, handler2)
|
||||
|
||||
expect(active.arr).toBe('object value 2')
|
||||
expect(active.arr).toEqual(['array value 1', 'array value 2'])
|
||||
|
||||
proxy2.obj.key = 'test'
|
||||
proxy2.arr[0] = 'test 2'
|
||||
|
||||
expect(active.obj.key).toBe('test')
|
||||
expect(active.arr).toEqual(['array value 1', 'test 2'])
|
||||
|
||||
proxy2.obj = { key: 'test again' }
|
||||
proxy1.arr = ['test 1']
|
||||
|
||||
expect(active.obj.key).toBe('test again')
|
||||
expect(shadow.obj.key.length).toBe(2)
|
||||
|
||||
remove(context2)
|
||||
|
||||
expect(active.obj.key).toBe('object value 1')
|
||||
expect(active.arr).toEqual(['test 1', 'test 2'])
|
||||
expect(shadow.arr.length).toBe(2)
|
||||
|
||||
remove(context1)
|
||||
|
||||
// TODO: should we clean up the obj ref too?
|
||||
expect(active.obj).toEqual({})
|
||||
expect(active.arr).toEqual(['test 2'])
|
||||
|
||||
expect(active.obj.key).toBeUndefined()
|
||||
expect(shadow.obj.key.length).toBe(0)
|
||||
delete proxy2.arr
|
||||
|
||||
// TODO: should we clean up the obj ref too?
|
||||
expect(active.arr).toBeUndefined()
|
||||
expect(shadow.arr.length).toBe(0)
|
||||
|
||||
proxy1.arr = ['test again 1']
|
||||
expect(active.arr).toEqual(['test again 1'])
|
||||
|
||||
proxy2.arr = []
|
||||
proxy2.arr[0] = 'test again 2'
|
||||
expect(active.arr).toEqual(['test again 1', 'test again 2'])
|
||||
})
|
||||
|
||||
test.skip('resolve (collection)', () => {
|
||||
test('resolve (collection)', () => {
|
||||
const target1 = {
|
||||
arr: [
|
||||
{ key: 'collection value 1' }
|
||||
{ key: 'collection value 1.1' },
|
||||
{ vmid: 'a', key: 'collection value 1.2' }
|
||||
]
|
||||
}
|
||||
|
||||
const target2 = {
|
||||
arr: [
|
||||
{ key: 'collection value 2' }
|
||||
{ vmid: 'a', key: 'collection value 2.1' },
|
||||
{ vmid: 'b', key: 'collection value 2.2' }
|
||||
]
|
||||
}
|
||||
|
||||
// Set initial value
|
||||
// Set initial value & init proxy
|
||||
setByObject(context1, target1)
|
||||
|
||||
// Init proxy
|
||||
const handler1 = createHandler(context1)
|
||||
const proxy1 = createProxy(target1, handler1)
|
||||
|
||||
@@ -176,22 +214,56 @@ describe('resolve', () => {
|
||||
const handler2 = createHandler(context2)
|
||||
const proxy2 = createProxy(target2, handler2)
|
||||
|
||||
expect(active.str).toBe('string value 2')
|
||||
expect(active.arr).toEqual([
|
||||
{ key: 'collection value 1.1' },
|
||||
{ vmid: 'a', key: 'collection value 2.1' },
|
||||
{ vmid: 'b', key: 'collection value 2.2' }
|
||||
])
|
||||
|
||||
proxy2.str = 'test'
|
||||
proxy1.arr[0].key = 'test 1.1'
|
||||
proxy1.arr[1].key = 'test 1.2'
|
||||
|
||||
expect(active.str).toBe('test')
|
||||
expect(active.arr).toEqual([
|
||||
{ key: 'test 1.1' },
|
||||
{ vmid: 'a', key: 'test 1.2' }, // TODO: this is WRONG, should be collection value 2.1 => setting a prop in a collection needs to trigger the resolveActive for the parent array
|
||||
{ vmid: 'b', key: 'collection value 2.2' }
|
||||
])
|
||||
|
||||
expect(shadow.str).toBeInstanceOf(Array)
|
||||
expect(shadow.str.length).toBe(2)
|
||||
proxy2.arr = [
|
||||
{ vmid: 'b', key: 'collection value 2.1' },
|
||||
{ vmid: 'c', key: 'collection value 2.2' }
|
||||
]
|
||||
|
||||
remove(context2)
|
||||
expect(active.arr).toEqual([
|
||||
{ key: 'test 1.1' },
|
||||
{ vmid: 'a', key: 'test 1.2' },
|
||||
{ vmid: 'b', key: 'collection value 2.1' },
|
||||
{ vmid: 'c', key: 'collection value 2.2' }
|
||||
])
|
||||
|
||||
expect(active.str).toBe('string value 1')
|
||||
expect(shadow.arr.length).toBe(2)
|
||||
|
||||
remove(context1)
|
||||
|
||||
expect(active.str).toBeUndefined()
|
||||
expect(shadow.str.length).toBe(0)
|
||||
expect(active.arr).toEqual([
|
||||
{ vmid: 'b', key: 'collection value 2.1' },
|
||||
{ vmid: 'c', key: 'collection value 2.2' }
|
||||
])
|
||||
|
||||
delete proxy2.arr
|
||||
|
||||
// TODO: should we clean up the obj ref too?
|
||||
expect(active.arr).toBeUndefined()
|
||||
expect(shadow.arr.length).toBe(0)
|
||||
|
||||
proxy1.arr = [{ vmid: 'a', key: 'test again 1' }]
|
||||
expect(active.arr).toEqual([{ vmid: 'a', key: 'test again 1' }])
|
||||
|
||||
// TODO: fix
|
||||
proxy2.arr = []
|
||||
proxy2.arr[0] = { vmid: 'a', value: 'test again 2' }
|
||||
expect(active.arr).toEqual([
|
||||
{ vmid: 'a', key: 'test again 2' }
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user