2
0
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:
pimlie
2020-11-16 00:07:54 +01:00
parent b61b44d5a8
commit 5c4ee7a547
4 changed files with 157 additions and 52 deletions
+24 -7
View File
@@ -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
}
}
}
+23 -4
View File
@@ -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
}
-3
View File
@@ -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
View File
@@ -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' }
])
})
})