Handle nulls / empty in views and tables
This commit is contained in:
parent
a65a6d5909
commit
0fb4613b5a
|
@ -28,9 +28,27 @@
|
|||
$: type = schema?.type ?? "string"
|
||||
$: customRenderer = customRenderers?.find(x => x.column === schema?.name)
|
||||
$: 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>
|
||||
|
||||
{#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>
|
||||
<slot />
|
||||
</svelte:component>
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
<script>
|
||||
export let value
|
||||
export let secondary
|
||||
</script>
|
||||
|
||||
<div>{typeof value === "object" ? JSON.stringify(value) : value}</div>
|
||||
<div class={secondary ? "secondary" : ""}>
|
||||
{typeof value === "object" ? JSON.stringify(value) : value}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
div {
|
||||
|
@ -10,4 +13,8 @@
|
|||
text-overflow: ellipsis;
|
||||
width: 150px;
|
||||
}
|
||||
.secondary {
|
||||
font-style: italic;
|
||||
color: var(--grey-5);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -42,6 +42,14 @@
|
|||
name: "Contains",
|
||||
key: "CONTAINS",
|
||||
},
|
||||
{
|
||||
name: "Is Set",
|
||||
key: "SET",
|
||||
},
|
||||
{
|
||||
name: "Is Not Set",
|
||||
key: "NOT_SET",
|
||||
},
|
||||
]
|
||||
|
||||
const CONJUNCTIONS = [
|
||||
|
@ -116,6 +124,10 @@
|
|||
|
||||
const getOptionLabel = x => x.name
|
||||
const getOptionValue = x => x.key
|
||||
|
||||
const showValue = filter => {
|
||||
return !(filter.condition === "SET" || filter.condition === "NOT_SET")
|
||||
}
|
||||
</script>
|
||||
|
||||
<ModalContent title="Filter" confirmText="Save" onConfirm={saveView} size="L">
|
||||
|
@ -144,30 +156,36 @@
|
|||
{getOptionLabel}
|
||||
{getOptionValue}
|
||||
/>
|
||||
{#if filter.key && isMultipleChoice(filter.key)}
|
||||
<Select
|
||||
bind:value={filter.value}
|
||||
options={fieldOptions(filter.key)}
|
||||
getOptionLabel={x => x.toString()}
|
||||
/>
|
||||
{:else if filter.key && isDate(filter.key)}
|
||||
<DatePicker
|
||||
bind:value={filter.value}
|
||||
placeholder={filter.key || fields[0]}
|
||||
/>
|
||||
{:else if filter.key && isNumber(filter.key)}
|
||||
<Input
|
||||
bind:value={filter.value}
|
||||
placeholder={filter.key || fields[0]}
|
||||
type="number"
|
||||
/>
|
||||
{#if showValue(filter)}
|
||||
{#if filter.key && isMultipleChoice(filter.key)}
|
||||
<Select
|
||||
bind:value={filter.value}
|
||||
options={fieldOptions(filter.key)}
|
||||
getOptionLabel={x => x.toString()}
|
||||
/>
|
||||
{:else if filter.key && isDate(filter.key)}
|
||||
<DatePicker
|
||||
bind:value={filter.value}
|
||||
placeholder={filter.key || fields[0]}
|
||||
/>
|
||||
{:else if filter.key && isNumber(filter.key)}
|
||||
<Input
|
||||
bind:value={filter.value}
|
||||
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}
|
||||
<Input
|
||||
placeholder={filter.key || fields[0]}
|
||||
bind:value={filter.value}
|
||||
/>
|
||||
<Icon hoverable name="Close" on:click={() => removeFilter(idx)} />
|
||||
<!-- empty div to preserve spacing -->
|
||||
<div />
|
||||
{/if}
|
||||
<Icon hoverable name="Close" on:click={() => removeFilter(idx)} />
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
|
|
|
@ -37,7 +37,11 @@ interface RunConfig {
|
|||
|
||||
module External {
|
||||
const { makeExternalQuery } = require("./utils")
|
||||
const { DataSourceOperation, FieldTypes, RelationshipTypes } = require("../../../constants")
|
||||
const {
|
||||
DataSourceOperation,
|
||||
FieldTypes,
|
||||
RelationshipTypes,
|
||||
} = require("../../../constants")
|
||||
const { breakExternalTableId, isSQL } = require("../../../integrations/utils")
|
||||
const { processObjectSync } = require("@budibase/string-templates")
|
||||
const { cloneDeep } = require("lodash/fp")
|
||||
|
|
|
@ -10,6 +10,22 @@ const TOKEN_MAP = {
|
|||
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 = {
|
||||
group: {
|
||||
type: "string",
|
||||
|
@ -72,6 +88,10 @@ function parseFilterExpression(filters) {
|
|||
expression.push(
|
||||
`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 {
|
||||
const value =
|
||||
typeof filter.value == "string" ? `"${filter.value}"` : filter.value
|
||||
|
|
|
@ -201,7 +201,13 @@ function buildRead(knex: Knex, json: QueryJson, limit: number): KnexQuery {
|
|||
[tableName]: query,
|
||||
}).select(selectStatement)
|
||||
// handle joins
|
||||
return addRelationships(knex, preQuery, selectStatement, tableName, relationships)
|
||||
return addRelationships(
|
||||
knex,
|
||||
preQuery,
|
||||
selectStatement,
|
||||
tableName,
|
||||
relationships
|
||||
)
|
||||
}
|
||||
|
||||
function buildUpdate(
|
||||
|
|
|
@ -34,7 +34,10 @@ export function generateRowIdField(keyProps: 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) {
|
||||
|
|
Loading…
Reference in New Issue