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