Allow fields to dynamically update validation rules so that other fields can be referenced

This commit is contained in:
Andrew Kingston 2021-08-11 15:07:06 +01:00
parent cebfcb280b
commit 99cb7c8789
2 changed files with 47 additions and 6 deletions

View File

@ -1,7 +1,7 @@
<script> <script>
import Placeholder from "../Placeholder.svelte" import Placeholder from "../Placeholder.svelte"
import FieldGroupFallback from "./FieldGroupFallback.svelte" import FieldGroupFallback from "./FieldGroupFallback.svelte"
import { getContext } from "svelte" import { getContext, onMount } from "svelte"
export let label export let label
export let field export let field
@ -34,6 +34,9 @@
fieldApi = formField?.fieldApi fieldApi = formField?.fieldApi
fieldSchema = formField?.fieldSchema fieldSchema = formField?.fieldSchema
// Keep validation rules up to date
$: fieldApi?.updateValidation(validation)
// Extract label position from field group context // Extract label position from field group context
$: labelPositionClass = $: labelPositionClass =
labelPosition === "above" ? "" : `spectrum-FieldLabel--${labelPosition}` labelPosition === "above" ? "" : `spectrum-FieldLabel--${labelPosition}`

View File

@ -36,7 +36,7 @@
// Create validation function based on field schema // Create validation function based on field schema
const schemaConstraints = schema?.[field]?.constraints const schemaConstraints = schema?.[field]?.constraints
const validate = createValidatorFromConstraints( const validator = createValidatorFromConstraints(
schemaConstraints, schemaConstraints,
validationRules, validationRules,
field, field,
@ -47,10 +47,11 @@
fieldMap[field] = { fieldMap[field] = {
fieldState: makeFieldState( fieldState: makeFieldState(
field, field,
validator,
defaultValue, defaultValue,
disabled || fieldDisabled || isAutoColumn disabled || fieldDisabled || isAutoColumn
), ),
fieldApi: makeFieldApi(field, defaultValue, validate), fieldApi: makeFieldApi(field, defaultValue),
fieldSchema: schema?.[field] ?? {}, fieldSchema: schema?.[field] ?? {},
} }
@ -93,9 +94,11 @@
] ]
// Creates an API for a specific field // Creates an API for a specific field
const makeFieldApi = (field, defaultValue, validate) => { const makeFieldApi = field => {
// Sets the value for a certain field and invokes validation
const setValue = (value, skipCheck = false) => { const setValue = (value, skipCheck = false) => {
const { fieldState } = fieldMap[field] const { fieldState } = fieldMap[field]
const { defaultValue, validator } = get(fieldState)
// Skip if the value is the same // Skip if the value is the same
if (!skipCheck && get(fieldState).value === value) { if (!skipCheck && get(fieldState).value === value) {
@ -103,7 +106,7 @@
} }
const newValue = value == null ? defaultValue : value const newValue = value == null ? defaultValue : value
const newError = validate ? validate(newValue) : null const newError = validator ? validator(newValue) : null
// Update field state // Update field state
fieldState.update(state => { fieldState.update(state => {
@ -127,15 +130,20 @@
return !newError return !newError
} }
// Clears the value of a certain field back to the initial value
const clearValue = () => { const clearValue = () => {
const { fieldState } = fieldMap[field] const { fieldState } = fieldMap[field]
const { defaultValue } = get(fieldState)
const newValue = initialValues[field] ?? defaultValue const newValue = initialValues[field] ?? defaultValue
// Update field state
fieldState.update(state => { fieldState.update(state => {
state.value = newValue state.value = newValue
state.error = null state.error = null
return state return state
}) })
// Update form state
formState.update(state => { formState.update(state => {
state.values = { ...state.values, [field]: newValue } state.values = { ...state.values, [field]: newValue }
delete state.errors[field] delete state.errors[field]
@ -144,9 +152,37 @@
}) })
} }
// Updates the validator rules for a certain field
const updateValidation = validationRules => {
const { fieldState } = fieldMap[field]
const { value, error } = get(fieldState)
// Create new validator
const schemaConstraints = schema?.[field]?.constraints
const validator = createValidatorFromConstraints(
schemaConstraints,
validationRules,
field,
table
)
// Update validator
fieldState.update(state => {
state.validator = validator
return state
})
// If there is currently an error, run the validator again in case
// the error should be cleared by the new validation rules
if (error) {
setValue(value, true)
}
}
return { return {
setValue, setValue,
clearValue, clearValue,
updateValidation,
validate: () => { validate: () => {
const { fieldState } = fieldMap[field] const { fieldState } = fieldMap[field]
setValue(get(fieldState).value, true) setValue(get(fieldState).value, true)
@ -155,13 +191,15 @@
} }
// Creates observable state data about a specific field // Creates observable state data about a specific field
const makeFieldState = (field, defaultValue, fieldDisabled) => { const makeFieldState = (field, validator, defaultValue, fieldDisabled) => {
return writable({ return writable({
field, field,
fieldId: `id-${generateID()}`, fieldId: `id-${generateID()}`,
value: initialValues[field] ?? defaultValue, value: initialValues[field] ?? defaultValue,
error: null, error: null,
disabled: fieldDisabled, disabled: fieldDisabled,
defaultValue,
validator,
}) })
} }