Evaluation of signal-libraries and their integration in frontend-frameworks with nested objects using js proxies.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

129 lines
4.8 KiB

<script setup lang="ts">
import { watch } from 'vue';
import useShape from '../../ng-mock/js-land/frontendAdapters/vue/useShape';
import { deepComputed } from '../../ng-mock/js-land/frontendAdapters/vue/deepComputed';
import flattenObject from '../utils/flattenObject';
// Acquire deep signal object (proxy) for a shape; scope second arg left empty string for parity
const shapeObj = useShape('TestShape', '');
// Expose for devtools exploration
// @ts-ignore
window.vueState = shapeObj;
// Helpers to read / write nested properties given a dot path produced by flattenObject
function getNestedValue(obj: any, path: string) {
return path.split('.').reduce((cur, k) => (cur == null ? cur : cur[k]), obj);
}
function setNestedValue(obj: any, path: string, value: any) {
const keys = path.split('.');
let cur = obj;
for (let i = 0; i < keys.length - 1; i++) {
cur = cur[keys[i]];
if (cur == null) return;
}
cur[keys[keys.length - 1]] = value;
}
// Reactive flattened entries built via deepComputed bridging deepSignal dependencies to Vue.
const flatEntries = deepComputed(() => (shapeObj ? flattenObject(shapeObj.value as any) : []));
// log flatEntries
watch(flatEntries, (newVal) => {
console.log('flatEntries changed:', newVal);
}, { deep: true });
</script>
<template>
<div class="vue">
<p>Rendered in Vue</p>
<template v-if="shapeObj">
<table border="1" cellpadding="5" style="margin-top:1rem; max-width:100%; font-size:0.9rem;">
<thead>
<tr>
<th>Key</th>
<th>Value</th>
<th>Edit</th>
</tr>
</thead>
<tbody>
<tr v-for="([key, value]) in flatEntries" :key="key">
<!-- Key-->
<td style="white-space:nowrap;">{{ key }}</td>
<!-- Value -->
<td>
<template v-if="value instanceof Set">
{{ Array.from(value).join(', ') }}
</template>
<template v-else-if="Array.isArray(value)">
[{{ value.join(', ') }}]
</template>
<template v-else>
{{ JSON.stringify(value) }}
</template>
</td>
<!-- Edit -->
<td>
<!-- String editing -->
<template v-if="typeof value === 'string'">
<template v-if="key.indexOf('.') === -1">
<input type="text" v-model="(shapeObj)[key]" />
</template>
<template v-else>
<input type="text" v-bind:value="value" />
</template>
</template>
<!-- Number editing -->
<template v-else-if="typeof value === 'number'">
<template v-if="key.indexOf('.') === -1">
<input type="number" v-model.number="(shapeObj)[key]" />
</template>
<template v-else>
<input type="number" v-bind:value="value" />
</template>
</template>
<!-- Boolean editing -->
<template v-else-if="typeof value === 'boolean'">
<template v-if="key.indexOf('.') === -1">
<input type="checkbox" v-model="(shapeObj as any)[key]" />
</template>
<template v-else>
<input type="checkbox" v-bind:value="value" />
</template>
</template>
<!-- Array editing -->
<template v-else-if="Array.isArray(value)">
<div style="display:flex; gap:.5rem;">
<button
@click="() => { const current = getNestedValue(shapeObj, key) || []; setNestedValue(shapeObj, key, [...current, current.length + 1]); }">Add</button>
<button
@click="() => { const current = getNestedValue(shapeObj, key) || []; if (current.length) setNestedValue(shapeObj, key, current.slice(0, -1)); }">Remove</button>
</div>
</template>
<!-- Set editing -->
<template v-else-if="value instanceof Set">
<div style="display:flex; gap:.5rem;">
<button
@click="() => { const currentSet: Set<any> = getNestedValue(shapeObj, key); currentSet.add(`item${currentSet.size + 1}`); }">Add</button>
<button
@click="() => { const currentSet: Set<any> = getNestedValue(shapeObj, key); const last = Array.from(currentSet).pop(); if (last !== undefined) currentSet.delete(last); }">Remove</button>
</div>
</template>
<template v-else>
N/A
</template>
</td>
</tr>
</tbody>
</table>
</template>
<template v-else>
<p>Loading state</p>
</template>
</div>
</template>