From f34c764391d8d2586c5796da6863589dcf07fb8f Mon Sep 17 00:00:00 2001 From: pk Date: Thu, 12 Jun 2025 12:53:09 +0200 Subject: [PATCH] Support for "$" syntax to indicate where the serialization logic should collect values into arrays. Use a dollar sign to indicate where tp-form should collect values as an array. For example `myfield.$list.value` collects { myfield: { list: [ { value: '...' } ] } } --- package.json | 2 +- tp-form.js | 84 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 80 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 0083c4e..41c036f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tp/tp-form", - "version": "1.1.1", + "version": "1.2.0", "description": "", "main": "tp-form.js", "scripts": { diff --git a/tp-form.js b/tp-form.js index 2accfa0..5adf09f 100644 --- a/tp-form.js +++ b/tp-form.js @@ -184,12 +184,86 @@ class TpForm extends LitElement { // Check if the object syntax is used in the elements name. if (el.name.indexOf('.') > -1) { const parts = el.name.split('.'); - parts.reduce((json, field, idx) => { - if (idx < parts.length - 1) { - json[field] = json[field] || {}; - return json[field]; + + // Check if any part has a $ prefix to indicate array collection + const arrayIndex = parts.findIndex(part => part.startsWith('$')); + + if (arrayIndex !== -1) { + // Remove the $ from the part name + parts[arrayIndex] = parts[arrayIndex].substring(1); + + // Build the path to the array location + let current = json; + for (let i = 0; i < arrayIndex; i++) { + current[parts[i]] = current[parts[i]] || {}; + current = current[parts[i]]; + } + + // The key where we want the array + const arrayKey = parts[arrayIndex]; + + // Initialize as array if needed + if (!current[arrayKey]) { + current[arrayKey] = []; + } else if (!Array.isArray(current[arrayKey])) { + // If it was previously a non-array, convert it + current[arrayKey] = [current[arrayKey]]; + } + + // Create the path for the remaining parts + const remainingParts = parts.slice(arrayIndex + 1); + + // Find an object in the array that doesn't have this value set yet + // or create a new one + let targetObj = current[arrayKey].find(item => { + if (remainingParts.length === 0) { + // If no remaining parts, we're adding directly to the array + return false; // Always add a new item + } + + // Check if this object doesn't have the property yet + let obj = item; + for (let i = 0; i < remainingParts.length - 1; i++) { + if (!obj[remainingParts[i]]) { + return true; // This object doesn't have the path, use it + } + obj = obj[remainingParts[i]]; + } + + return !obj[remainingParts[remainingParts.length - 1]]; + }); + + // If no suitable object found, create a new one + if (!targetObj) { + targetObj = {}; + current[arrayKey].push(targetObj); + } + + // Add the value to the target object + if (remainingParts.length === 0) { + // Direct array of values + // Just push the value (we already created a new entry above) + current[arrayKey][current[arrayKey].length - 1] = el.value !== null && el.value !== undefined ? el.value : ''; } else { - this._addToJson(field, el.value, json); + // Add the value at the nested path + let objCursor = targetObj; + for (let i = 0; i < remainingParts.length - 1; i++) { + objCursor[remainingParts[i]] = objCursor[remainingParts[i]] || {}; + objCursor = objCursor[remainingParts[i]]; + } + objCursor[remainingParts[remainingParts.length - 1]] = el.value !== null && el.value !== undefined ? el.value : ''; + } + + return; + } + + // Default behavior (no $ in the path) + parts.reduce((currentJson, field, idx) => { + if (idx < parts.length - 1) { + currentJson[field] = currentJson[field] || {}; + return currentJson[field]; + } else { + this._addToJson(field, el.value, currentJson); } }, json);