Add form support for fields which can only use bindings rather than explicit onchange handlers

This commit is contained in:
Andrew Kingston 2021-02-01 11:14:24 +00:00
parent 4630699f13
commit c9bf2700e3
5 changed files with 53 additions and 48 deletions

View File

@ -1,27 +1,28 @@
<script>
import SpectrumField from "./SpectrumField.svelte"
import Dropzone from "../attachments/Dropzone.svelte"
import { onMount } from "svelte"
export let field
export let label
let previousFiles = []
let files = []
$: {
// Only actually update the value when it changes, so that we don't trigger
// validation unnecessarily
if (files !== previousFiles) {
fieldApi?.setValue(files)
previousFiles = files
}
}
let fieldState
let fieldApi
// Update form value from bound value after we've mounted
let value
let mounted = false
$: mounted && fieldApi?.setValue(value)
// Get the fields initial value after initialising
onMount(() => {
value = $fieldState?.value
mounted = true
})
</script>
<SpectrumField {label} {field} bind:fieldState bind:fieldApi>
{#if fieldState}
<Dropzone bind:files />
<SpectrumField {label} {field} bind:fieldState bind:fieldApi defaultValue={[]}>
{#if mounted}
<Dropzone bind:files={value} />
{/if}
</SpectrumField>

View File

@ -13,18 +13,19 @@
const onChange = event => {
fieldApi.setValue(event.target.checked)
}
// Ensure a valid boolean value is set
onMount(() => {
fieldApi.setValue($fieldState.value || false)
})
</script>
<SpectrumField {label} {field} bind:fieldState bind:fieldApi>
<SpectrumField
{label}
{field}
bind:fieldState
bind:fieldApi
defaultValue={false}>
{#if fieldState}
<div class="spectrum-FieldGroup spectrum-FieldGroup--horizontal">
<label class="spectrum-Checkbox" class:is-invalid={!$fieldState.valid}>
<input
checked={$fieldState.value}
on:change={onChange}
type="checkbox"
class="spectrum-Checkbox-input"

View File

@ -34,7 +34,7 @@
// Form API contains functions to control the form
const formApi = {
registerField: field => {
registerField: (field, defaultValue = null) => {
if (!field) {
return
}
@ -47,8 +47,8 @@
const validate = createValidatorFromConstraints(constraints, field, table)
fieldMap[field] = {
fieldState: makeFieldState(field),
fieldApi: makeFieldApi(field, validate),
fieldState: makeFieldState(field, defaultValue),
fieldApi: makeFieldApi(field, defaultValue, validate),
fieldSchema: schema?.[field] ?? {},
}
fieldMap = fieldMap
@ -60,13 +60,16 @@
setContext("form", { formApi, formState })
// Creates an API for a specific field
const makeFieldApi = (field, validate) => {
const makeFieldApi = (field, defaultValue, validate) => {
return {
setValue: value => {
const { fieldState } = fieldMap[field]
fieldState.update(state => {
state.value = value
state.error = validate ? validate(value) : null
if (state.value === value) {
return state
}
state.value = value == null ? defaultValue : value
state.error = validate ? validate(state.value) : null
state.valid = !state.error
return state
})
@ -76,13 +79,13 @@
}
// Creates observable state data about a specific field
const makeFieldState = field => {
const makeFieldState = (field, defaultValue) => {
return writable({
field,
fieldId: `${Math.random()
.toString(32)
.substr(2)}/${field}`,
value: initialValues[field] ?? null,
value: initialValues[field] ?? defaultValue,
error: null,
valid: true,
})

View File

@ -1,4 +1,5 @@
<script>
import { onMount } from "svelte"
import { RichText } from "@budibase/bbui"
import SpectrumField from "./SpectrumField.svelte"
@ -8,17 +9,17 @@
let fieldState
let fieldApi
let previousValue = ""
let value = ""
$: {
// Only actually update the value when it changes, so that we don't trigger
// validation unnecessarily
if (value !== previousValue) {
fieldApi?.setValue(value)
previousValue = value
}
}
// Update form value from bound value after we've mounted
let value
let mounted = false
$: mounted && fieldApi?.setValue(value)
// Get the fields initial value after initialising
onMount(() => {
value = $fieldState?.value
mounted = true
})
// Options for rich text component
const options = {
@ -37,8 +38,8 @@
}
</script>
<SpectrumField {label} {field} bind:fieldState bind:fieldApi>
{#if fieldState}
<SpectrumField {label} {field} bind:fieldState bind:fieldApi defaultValue="">
{#if mounted}
<div>
<RichText bind:value {options} />
</div>

View File

@ -8,6 +8,7 @@
export let fieldState
export let fieldApi
export let fieldSchema
export let defaultValue
// Get contexts
const formContext = getContext("form")
@ -16,16 +17,14 @@
const component = getContext("component")
// Register field with form
$: formApi = formContext?.formApi
$: labelPosition = fieldGroupContext?.labelPosition || "above"
$: formField = formApi?.registerField(field)
const formApi = formContext?.formApi
const labelPosition = fieldGroupContext?.labelPosition || "above"
const formField = formApi?.registerField(field, defaultValue)
// Expose field properties to parent component
$: {
fieldState = formField?.fieldState
fieldApi = formField?.fieldApi
fieldSchema = formField?.fieldSchema
}
// Extract label position from field group context
$: labelPositionClass =