diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/TableViewSelect.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/TableViewSelect.svelte index 274975770b..bf88c8e9eb 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/TableViewSelect.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/TableViewSelect.svelte @@ -37,6 +37,7 @@ $: queries = $backendUiStore.queries.map(query => ({ label: query.name, name: query.name, + tableId: query._id, ...query, schema: query.schema, parameters: query.parameters, @@ -59,9 +60,12 @@ providerId: property.providerId, label: property.readableBinding, fieldName: property.fieldSchema.name, - name: `all_${property.fieldSchema.tableId}`, tableId: property.fieldSchema.tableId, type: "link", + // These properties will be enriched by the client library and provide + // details of the parent row of the relationship field, from context + rowId: `{{ ${property.providerId}._id }}`, + rowTableId: `{{ ${property.providerId}.tableId }}`, } }) @@ -84,12 +88,12 @@ class="dropdownbutton" bind:this={anchorRight} on:click={dropdownRight.show}> - {value.label ? value.label : 'Table / View / Query'} + {value?.label ? value.label : 'Choose option'} -{#if value.type === 'query'} +{#if value?.type === 'query'} - + { +export const fetchDatasource = async datasource => { if (!datasource || !datasource.type) { return [] } @@ -23,28 +21,22 @@ export const fetchDatasource = async (datasource, dataContext) => { } else if (type === "view") { rows = await fetchViewData(datasource) } else if (type === "query") { - const bindings = get(bindingStore) - // Set the default query params - let queryParams = datasource.queryParams || {} + let parameters = cloneDeep(datasource.queryParams || {}) for (let param of datasource.parameters) { - if (!queryParams[param.name]) { - queryParams[param.name] = param.default + if (!parameters[param.name]) { + parameters[param.name] = param.default } } - const parameters = enrichDataBindings(queryParams, { - ...bindings, - ...dataContext, - }) return await executeQuery({ queryId: datasource._id, parameters }) } else if (type === "link") { - const row = dataContext[datasource.providerId] rows = await fetchRelationshipData({ - rowId: row?._id, - tableId: row?.tableId, + rowId: datasource.rowId, + tableId: datasource.rowTableId, fieldName, }) } - // Enrich rows + + // Enrich rows so they can displayed properly return await enrichRows(rows, tableId) } diff --git a/packages/client/src/utils/enrichDataBinding.js b/packages/client/src/utils/enrichDataBinding.js index 5de6b31a89..16a1724fd9 100644 --- a/packages/client/src/utils/enrichDataBinding.js +++ b/packages/client/src/utils/enrichDataBinding.js @@ -1,3 +1,4 @@ +import { cloneDeep } from "lodash/fp" import mustache from "mustache" // this is a much more liberal version of mustache's escape function @@ -35,12 +36,33 @@ export const enrichDataBinding = (input, context) => { } /** - * Enriches each prop in a props object + * Recursively enriches all props in a props object and returns the new props. + * Props are deeply cloned so that no mutation is done to the source object. */ export const enrichDataBindings = (props, context) => { - let enrichedProps = {} - Object.entries(props).forEach(([key, value]) => { - enrichedProps[key] = enrichDataBinding(value, context) - }) - return enrichedProps + let clonedProps = cloneDeep(props) + recursiveEnrich(clonedProps, context) + return clonedProps +} + +/** + * Recurses through an object and enriches all string props found. + */ +const recursiveEnrich = (props, context) => { + if (typeof props !== "object") { + return + } + let keys = [] + if (Array.isArray(props)) { + keys = Array.from(props.keys()) + } else if (typeof props === "object") { + keys = Object.keys(props || {}) + } + keys.forEach(key => { + if (typeof props[key] === "string") { + props[key] = enrichDataBinding(props[key], context) + } else { + recursiveEnrich(props[key], context) + } + }) } diff --git a/packages/standard-components/src/List.svelte b/packages/standard-components/src/List.svelte index 1a5638404a..e7f03ca851 100644 --- a/packages/standard-components/src/List.svelte +++ b/packages/standard-components/src/List.svelte @@ -1,26 +1,21 @@ diff --git a/packages/standard-components/src/charts/BarChart.svelte b/packages/standard-components/src/charts/BarChart.svelte index 4bf01b9a21..51bc91046e 100644 --- a/packages/standard-components/src/charts/BarChart.svelte +++ b/packages/standard-components/src/charts/BarChart.svelte @@ -35,7 +35,7 @@ // Fetch, filter and sort data const schema = (await API.fetchTableDefinition(datasource.tableId)).schema - const result = await API.fetchDatasource(datasource, $dataContext) + const result = await API.fetchDatasource(datasource) const reducer = row => (valid, column) => valid && row[column] != null const hasAllColumns = row => allCols.reduce(reducer(row), true) const data = result diff --git a/packages/standard-components/src/charts/CandleStickChart.svelte b/packages/standard-components/src/charts/CandleStickChart.svelte index 341b1d714f..fbad22feaf 100644 --- a/packages/standard-components/src/charts/CandleStickChart.svelte +++ b/packages/standard-components/src/charts/CandleStickChart.svelte @@ -33,7 +33,7 @@ // Fetch, filter and sort data const schema = (await API.fetchTableDefinition(datasource.tableId)).schema - const result = await API.fetchDatasource(datasource, $dataContext) + const result = await API.fetchDatasource(datasource) const reducer = row => (valid, column) => valid && row[column] != null const hasAllColumns = row => allCols.reduce(reducer(row), true) const data = result diff --git a/packages/standard-components/src/charts/LineChart.svelte b/packages/standard-components/src/charts/LineChart.svelte index 6e7f7f65f0..256ec70fe6 100644 --- a/packages/standard-components/src/charts/LineChart.svelte +++ b/packages/standard-components/src/charts/LineChart.svelte @@ -41,7 +41,7 @@ // Fetch, filter and sort data const schema = (await API.fetchTableDefinition(datasource.tableId)).schema - const result = await API.fetchDatasource(datasource, $dataContext) + const result = await API.fetchDatasource(datasource) const reducer = row => (valid, column) => valid && row[column] != null const hasAllColumns = row => allCols.reduce(reducer(row), true) const data = result diff --git a/packages/standard-components/src/charts/PieChart.svelte b/packages/standard-components/src/charts/PieChart.svelte index 9a8db6b17a..65ac8ac490 100644 --- a/packages/standard-components/src/charts/PieChart.svelte +++ b/packages/standard-components/src/charts/PieChart.svelte @@ -31,7 +31,7 @@ // Fetch, filter and sort data const schema = (await API.fetchTableDefinition(datasource.tableId)).schema - const result = await API.fetchDatasource(datasource, $dataContext) + const result = await API.fetchDatasource(datasource) const data = result .filter(row => row[labelColumn] != null && row[valueColumn] != null) .slice(0, 20) diff --git a/packages/standard-components/src/grid/Component.svelte b/packages/standard-components/src/grid/Component.svelte index 0d1380d8a7..3492301082 100644 --- a/packages/standard-components/src/grid/Component.svelte +++ b/packages/standard-components/src/grid/Component.svelte @@ -3,7 +3,7 @@ import { number } from "./valueSetters" import { getRenderer } from "./customRenderer" import { isEmpty } from "lodash/fp" - import { getContext, onMount } from "svelte" + import { getContext } from "svelte" import AgGrid from "@budibase/svelte-ag-grid" import { TextButton as DeleteButton, @@ -34,6 +34,7 @@ ["--grid-height"]: `${height}px`, }, } + $: fetchData(datasource) // These can never change at runtime so don't need to be reactive let canEdit = editable && datasource && datasource.type !== "view" @@ -57,8 +58,11 @@ pagination, } - async function fetchData() { - data = await API.fetchDatasource(datasource, $dataContext) + async function fetchData(datasource) { + if (isEmpty(datasource)) { + return + } + data = await API.fetchDatasource(datasource) let schema @@ -115,12 +119,6 @@ dataLoaded = true } - $: datasource && fetchData() - - onMount(() => { - if (!isEmpty(datasource)) fetchData() - }) - const shouldHideField = name => { if (name.startsWith("_")) return true // always 'row'