Handle nulls / empty in views and tables
This commit is contained in:
parent
65077556b1
commit
b88319d201
|
@ -28,9 +28,27 @@
|
||||||
$: type = schema?.type ?? "string"
|
$: type = schema?.type ?? "string"
|
||||||
$: customRenderer = customRenderers?.find(x => x.column === schema?.name)
|
$: customRenderer = customRenderers?.find(x => x.column === schema?.name)
|
||||||
$: renderer = customRenderer?.component ?? typeMap[type] ?? StringRenderer
|
$: renderer = customRenderer?.component ?? typeMap[type] ?? StringRenderer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't use falsy here as we want to:
|
||||||
|
* - include empty arrays
|
||||||
|
* - exclude 0 and booleans
|
||||||
|
*
|
||||||
|
* If updated, the corresponding view expression should be updated in 'server/viewBuilder.js'
|
||||||
|
*/
|
||||||
|
const isNotSet = value => {
|
||||||
|
return (
|
||||||
|
value === undefined ||
|
||||||
|
value === null ||
|
||||||
|
value === "" ||
|
||||||
|
(Array.isArray(value) && value.length === 0)
|
||||||
|
)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if renderer && (customRenderer || (value != null && value !== ""))}
|
{#if !customRenderer && isNotSet(value)}
|
||||||
|
<svelte:component this={StringRenderer} value={"Not Set"} secondary={true} />
|
||||||
|
{:else if renderer}
|
||||||
<svelte:component this={renderer} {row} {schema} {value} on:clickrelationship>
|
<svelte:component this={renderer} {row} {schema} {value} on:clickrelationship>
|
||||||
<slot />
|
<slot />
|
||||||
</svelte:component>
|
</svelte:component>
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
<script>
|
<script>
|
||||||
export let value
|
export let value
|
||||||
|
export let secondary
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>{typeof value === "object" ? JSON.stringify(value) : value}</div>
|
<div class={secondary ? "secondary" : ""}>
|
||||||
|
{typeof value === "object" ? JSON.stringify(value) : value}
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
div {
|
div {
|
||||||
|
@ -10,4 +13,8 @@
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
.secondary {
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--grey-5);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -42,6 +42,14 @@
|
||||||
name: "Contains",
|
name: "Contains",
|
||||||
key: "CONTAINS",
|
key: "CONTAINS",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Is Set",
|
||||||
|
key: "SET",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Is Not Set",
|
||||||
|
key: "NOT_SET",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const CONJUNCTIONS = [
|
const CONJUNCTIONS = [
|
||||||
|
@ -116,6 +124,10 @@
|
||||||
|
|
||||||
const getOptionLabel = x => x.name
|
const getOptionLabel = x => x.name
|
||||||
const getOptionValue = x => x.key
|
const getOptionValue = x => x.key
|
||||||
|
|
||||||
|
const showValue = filter => {
|
||||||
|
return !(filter.condition === "SET" || filter.condition === "NOT_SET")
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ModalContent title="Filter" confirmText="Save" onConfirm={saveView} size="L">
|
<ModalContent title="Filter" confirmText="Save" onConfirm={saveView} size="L">
|
||||||
|
@ -144,30 +156,36 @@
|
||||||
{getOptionLabel}
|
{getOptionLabel}
|
||||||
{getOptionValue}
|
{getOptionValue}
|
||||||
/>
|
/>
|
||||||
{#if filter.key && isMultipleChoice(filter.key)}
|
{#if showValue(filter)}
|
||||||
<Select
|
{#if filter.key && isMultipleChoice(filter.key)}
|
||||||
bind:value={filter.value}
|
<Select
|
||||||
options={fieldOptions(filter.key)}
|
bind:value={filter.value}
|
||||||
getOptionLabel={x => x.toString()}
|
options={fieldOptions(filter.key)}
|
||||||
/>
|
getOptionLabel={x => x.toString()}
|
||||||
{:else if filter.key && isDate(filter.key)}
|
/>
|
||||||
<DatePicker
|
{:else if filter.key && isDate(filter.key)}
|
||||||
bind:value={filter.value}
|
<DatePicker
|
||||||
placeholder={filter.key || fields[0]}
|
bind:value={filter.value}
|
||||||
/>
|
placeholder={filter.key || fields[0]}
|
||||||
{:else if filter.key && isNumber(filter.key)}
|
/>
|
||||||
<Input
|
{:else if filter.key && isNumber(filter.key)}
|
||||||
bind:value={filter.value}
|
<Input
|
||||||
placeholder={filter.key || fields[0]}
|
bind:value={filter.value}
|
||||||
type="number"
|
placeholder={filter.key || fields[0]}
|
||||||
/>
|
type="number"
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<Input
|
||||||
|
placeholder={filter.key || fields[0]}
|
||||||
|
bind:value={filter.value}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
<Icon hoverable name="Close" on:click={() => removeFilter(idx)} />
|
||||||
{:else}
|
{:else}
|
||||||
<Input
|
<Icon hoverable name="Close" on:click={() => removeFilter(idx)} />
|
||||||
placeholder={filter.key || fields[0]}
|
<!-- empty div to preserve spacing -->
|
||||||
bind:value={filter.value}
|
<div />
|
||||||
/>
|
|
||||||
{/if}
|
{/if}
|
||||||
<Icon hoverable name="Close" on:click={() => removeFilter(idx)} />
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
|
|
|
@ -37,7 +37,11 @@ interface RunConfig {
|
||||||
|
|
||||||
module External {
|
module External {
|
||||||
const { makeExternalQuery } = require("./utils")
|
const { makeExternalQuery } = require("./utils")
|
||||||
const { DataSourceOperation, FieldTypes, RelationshipTypes } = require("../../../constants")
|
const {
|
||||||
|
DataSourceOperation,
|
||||||
|
FieldTypes,
|
||||||
|
RelationshipTypes,
|
||||||
|
} = require("../../../constants")
|
||||||
const { breakExternalTableId, isSQL } = require("../../../integrations/utils")
|
const { breakExternalTableId, isSQL } = require("../../../integrations/utils")
|
||||||
const { processObjectSync } = require("@budibase/string-templates")
|
const { processObjectSync } = require("@budibase/string-templates")
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
|
|
|
@ -10,6 +10,22 @@ const TOKEN_MAP = {
|
||||||
OR: "||",
|
OR: "||",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't use falsy here as we want to:
|
||||||
|
* - include empty arrays
|
||||||
|
* - exclude 0 and booleans
|
||||||
|
*
|
||||||
|
* If updated, the corresponding rendering condition should be updated in 'bbui/CellRenderer.svelte'
|
||||||
|
*/
|
||||||
|
const isNotSetExpression = key => {
|
||||||
|
return `(
|
||||||
|
doc["${key}"] === undefined ||
|
||||||
|
doc["${key}"] === null ||
|
||||||
|
doc["${key}"] === "" ||
|
||||||
|
(Array.isArray(doc["${key}"]) && doc["${key}"].length === 0)
|
||||||
|
)`
|
||||||
|
}
|
||||||
|
|
||||||
const GROUP_PROPERTY = {
|
const GROUP_PROPERTY = {
|
||||||
group: {
|
group: {
|
||||||
type: "string",
|
type: "string",
|
||||||
|
@ -72,6 +88,10 @@ function parseFilterExpression(filters) {
|
||||||
expression.push(
|
expression.push(
|
||||||
`doc["${filter.key}"].${TOKEN_MAP[filter.condition]}("${filter.value}")`
|
`doc["${filter.key}"].${TOKEN_MAP[filter.condition]}("${filter.value}")`
|
||||||
)
|
)
|
||||||
|
} else if (filter.condition === "NOT_SET") {
|
||||||
|
expression.push(isNotSetExpression(filter.key))
|
||||||
|
} else if (filter.condition === "SET") {
|
||||||
|
expression.push(`!${isNotSetExpression(filter.key)}`)
|
||||||
} else {
|
} else {
|
||||||
const value =
|
const value =
|
||||||
typeof filter.value == "string" ? `"${filter.value}"` : filter.value
|
typeof filter.value == "string" ? `"${filter.value}"` : filter.value
|
||||||
|
|
|
@ -201,7 +201,13 @@ function buildRead(knex: Knex, json: QueryJson, limit: number): KnexQuery {
|
||||||
[tableName]: query,
|
[tableName]: query,
|
||||||
}).select(selectStatement)
|
}).select(selectStatement)
|
||||||
// handle joins
|
// handle joins
|
||||||
return addRelationships(knex, preQuery, selectStatement, tableName, relationships)
|
return addRelationships(
|
||||||
|
knex,
|
||||||
|
preQuery,
|
||||||
|
selectStatement,
|
||||||
|
tableName,
|
||||||
|
relationships
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildUpdate(
|
function buildUpdate(
|
||||||
|
|
|
@ -34,7 +34,10 @@ export function generateRowIdField(keyProps: any[] = []) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isRowId(field: any) {
|
export function isRowId(field: any) {
|
||||||
return Array.isArray(field) || (typeof field === "string" && field.match(ROW_ID_REGEX) != null)
|
return (
|
||||||
|
Array.isArray(field) ||
|
||||||
|
(typeof field === "string" && field.match(ROW_ID_REGEX) != null)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertRowId(field: any) {
|
export function convertRowId(field: any) {
|
||||||
|
|
Loading…
Reference in New Issue