From 179ddf6ab75ff4a5ee9fcebb018a0bb2c39363f8 Mon Sep 17 00:00:00 2001
From: Jeremie Pardou <jeremie@baserow.io>
Date: Fri, 26 Jul 2024 17:56:18 +0200
Subject: [PATCH 1/2] Fix performance issues with input inside repeat

---
 ...ance_when_editing_default_value_of_in.json |  7 +++++
 .../modules/builder/store/formData.js         | 13 ++++++---
 web-frontend/modules/core/utils/object.js     | 28 +++++++++++++++++--
 3 files changed, 41 insertions(+), 7 deletions(-)
 create mode 100644 changelog/entries/unreleased/bug/builder_improve_performance_when_editing_default_value_of_in.json

diff --git a/changelog/entries/unreleased/bug/builder_improve_performance_when_editing_default_value_of_in.json b/changelog/entries/unreleased/bug/builder_improve_performance_when_editing_default_value_of_in.json
new file mode 100644
index 00000000000..f54ffc3f464
--- /dev/null
+++ b/changelog/entries/unreleased/bug/builder_improve_performance_when_editing_default_value_of_in.json
@@ -0,0 +1,7 @@
+{
+    "type": "bug",
+    "message": "[Builder] improve performance when editing default value of input element inside repeat element",
+    "issue_number": null,
+    "bullet_points": [],
+    "created_at": "2024-07-26"
+}
\ No newline at end of file
diff --git a/web-frontend/modules/builder/store/formData.js b/web-frontend/modules/builder/store/formData.js
index e3516e26c79..107709abb60 100644
--- a/web-frontend/modules/builder/store/formData.js
+++ b/web-frontend/modules/builder/store/formData.js
@@ -7,15 +7,19 @@ import {
 const state = {}
 
 /**
- * Responsible for setting a form entry at a given path in the form data of a page.
- * We use lodash's `set` in `setValueAtPath`. A shallow copy of `page.formData` is
- * returned with the updated value.
+ * Responsible for setting a form entry at a given path in the form data of a page in
+ * a reactive way.
+ *
  * @param {Object} page - The page object that holds the form data.
  * @param {String} uniqueElementId - The unique element id of the form element.
  * @param {Any} value - The value to set at the path.
  */
 function setFormEntryAtPath(page, uniqueElementId, value) {
-  page.formData = setValueAtPath({ ...page.formData }, uniqueElementId, value)
+  // We update properties per properties here to avoid updating the array
+  // and then trigger to many element update in repeat elements.
+  Object.entries(value).forEach(([key, value]) =>
+    setValueAtPath(page.formData, `${uniqueElementId}.${key}`, value)
+  )
 }
 
 const mutations = {
@@ -23,6 +27,7 @@ const mutations = {
     if (!page.formData) {
       Vue.set(page, 'formData', {})
     }
+
     setFormEntryAtPath(page, uniqueElementId, payload)
   },
   REMOVE_FORM_DATA(state, { page, elementId }) {
diff --git a/web-frontend/modules/core/utils/object.js b/web-frontend/modules/core/utils/object.js
index b424480c10e..3ee31f53519 100644
--- a/web-frontend/modules/core/utils/object.js
+++ b/web-frontend/modules/core/utils/object.js
@@ -1,4 +1,5 @@
 import _ from 'lodash'
+import Vue from 'vue'
 
 /**
  * Clones the provided JavaScript object and returns that one.
@@ -116,15 +117,36 @@ export function getValueAtPath(obj, path) {
 }
 
 /**
- * Responsible for setting a value at a given path in `obj`.
+ * Deeply sets a value in an object (or array) from a dotted path string.
+ * Creates any missing intermediate parts if necessary.
+ * Use Vue.set to keep reactivity.
  *
  * @param {Object} obj - The object we want to update.
  * @param {String} path - The path, delimited by periods, to the value.
  * @param {Any} value - The value to set at the path.
- * @returns {Object} The object with the updated value.
  */
 export function setValueAtPath(obj, path, value) {
-  return _.set(obj, path, value)
+  // Note: We can't use `_.set` or `_.setWith` because would update all intermediary
+  // level and that's not what we want.
+  const keys = path.split('.')
+  let current = obj
+
+  for (let i = 0; i < keys.length; i++) {
+    const key = keys[i]
+
+    // If we are at the last key, set the value
+    if (i === keys.length - 1) {
+      Vue.set(current, key, value)
+    } else {
+      // If the key does not exist or is not an object, create an empty object or array
+      if (!(key in current) || typeof current[key] !== 'object') {
+        // Check if the next key is a number to decide between object or array
+        Vue.set(current, key, isNaN(keys[i + 1]) ? {} : [])
+      }
+      // Move to the next level in the object
+      current = current[key]
+    }
+  }
 }
 
 /**
-- 
GitLab


From 6484ecc637131a4491bd4785b2d82f5c2ca8d641 Mon Sep 17 00:00:00 2001
From: Jeremie Pardou <jeremie@baserow.io>
Date: Tue, 30 Jul 2024 09:29:17 +0200
Subject: [PATCH 2/2] Fix form validation

---
 web-frontend/modules/builder/store/formData.js | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/web-frontend/modules/builder/store/formData.js b/web-frontend/modules/builder/store/formData.js
index 107709abb60..2deff951c6c 100644
--- a/web-frontend/modules/builder/store/formData.js
+++ b/web-frontend/modules/builder/store/formData.js
@@ -17,9 +17,13 @@ const state = {}
 function setFormEntryAtPath(page, uniqueElementId, value) {
   // We update properties per properties here to avoid updating the array
   // and then trigger to many element update in repeat elements.
-  Object.entries(value).forEach(([key, value]) =>
-    setValueAtPath(page.formData, `${uniqueElementId}.${key}`, value)
-  )
+  if (typeof value === 'object') {
+    Object.entries(value).forEach(([key, value]) =>
+      setValueAtPath(page.formData, `${uniqueElementId}.${key}`, value)
+    )
+  } else {
+    setValueAtPath(page.formData, uniqueElementId, value)
+  }
 }
 
 const mutations = {
-- 
GitLab