From 4ecc92de3b86cad8568e2be6f0c49c0123531aa7 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 30 Sep 2021 17:24:43 +0100 Subject: [PATCH] Updating data bindings, there were instances where it could loop/iterate over already replaced bindings, used a search technique to be a bit more specific about the replacements and make sure each binding could only be replaced once. --- .../builder/src/builderStore/dataBinding.js | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js index 0858b29bcb..dc1e40e517 100644 --- a/packages/builder/src/builderStore/dataBinding.js +++ b/packages/builder/src/builderStore/dataBinding.js @@ -1,12 +1,12 @@ import { cloneDeep } from "lodash/fp" import { get } from "svelte/store" import { + findAllMatchingComponents, findComponent, findComponentPath, - findAllMatchingComponents, } from "./storeUtils" import { store } from "builderStore" -import { tables as tablesStore, queries as queriesStores } from "stores/backend" +import { queries as queriesStores, tables as tablesStore } from "stores/backend" import { makePropSafe } from "@budibase/string-templates" import { TableNames } from "../constants" @@ -422,6 +422,10 @@ function shouldReplaceBinding(currentValue, from, convertTo) { return !invalids.find(invalid => noSpaces?.includes(invalid)) } +function replaceBetween(string, start, end, replacement) { + return string.substring(0, start) + replacement + string.substring(end) +} + /** * utility function for the readableToRuntimeBinding and runtimeToReadableBinding. */ @@ -431,6 +435,7 @@ function bindingReplacement(bindableProperties, textWithBindings, convertTo) { if (typeof textWithBindings !== "string") { return textWithBindings } + // work from longest to shortest const convertFromProps = bindableProperties .map(el => el[convertFrom]) .sort((a, b) => { @@ -440,12 +445,29 @@ function bindingReplacement(bindableProperties, textWithBindings, convertTo) { let result = textWithBindings for (let boundValue of boundValues) { let newBoundValue = boundValue + // we use a search string, where any time we replace something we blank it out + // in the search, working from longest to shortest so always use best match first + let searchString = newBoundValue for (let from of convertFromProps) { if (shouldReplaceBinding(newBoundValue, from, convertTo)) { const binding = bindableProperties.find(el => el[convertFrom] === from) - while (newBoundValue.includes(from)) { - newBoundValue = newBoundValue.replace(from, binding[convertTo]) - } + let idx + do { + // see if any instances of this binding exist in the search string + idx = searchString.indexOf(from) + if (idx !== -1) { + let end = idx + from.length, + searchReplace = Array(binding[convertTo].length).join("*") + // blank out parts of the search string + searchString = replaceBetween(searchString, idx, end, searchReplace) + newBoundValue = replaceBetween( + newBoundValue, + idx, + end, + binding[convertTo] + ) + } + } while (idx !== -1) } } result = result.replace(boundValue, newBoundValue)