Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nullable form field with constraints #454

Closed
snrmwg opened this issue Feb 26, 2024 · 4 comments
Closed

Nullable form field with constraints #454

snrmwg opened this issue Feb 26, 2024 · 4 comments
Assignees
Labels
question Further information is requested

Comments

@snrmwg
Copy link

snrmwg commented Feb 26, 2024

I would like to use Valibot to validate form fields. The input of text fields should be trimmed. If the result is an empty string the result should be null. For optional fields this would be the expected and accepted value. If there is text in the input field it should be possible to perform additional checks (i.e. minimum length or should be email). How would I do that?

@fabian-hiller
Copy link
Owner

fabian-hiller commented Feb 27, 2024

Have you read the documentation? Here is an example schema. Try it out in our playground.

import * as v from 'valibot';

const FormSchema = v.object({
  value: v.union([
    v.transform(v.string([v.regex(/^\s*$/u)]), () => null),
    v.string([v.toTrimmed(), v.minLength(3), v.maxLength(10)]),
  ]),
});

@fabian-hiller fabian-hiller self-assigned this Feb 27, 2024
@fabian-hiller fabian-hiller added the question Further information is requested label Feb 27, 2024
@snrmwg
Copy link
Author

snrmwg commented Feb 27, 2024

Of course I read the documentation 🙂
But I did not see the union for a solution. Thanks for your hint, Fabian.

I will try it this way:

const EmptyStringToNullTransform = transform(string([regex(/^\s*$/u)]), () => null)
const FormString = union([
    EmptyStringToNullTransform,
    string([toTrimmed()]),
  ])

const form = object({
  firstname: union([
    EmptyStringToNullTransform,
    string([toTrimmed()]),
  ]),
  lastname: string([toTrimmed(), minLength(2, 'too short')]),
  zipCode: union([
    EmptyStringToNullTransform,
    string([toTrimmed(), minLength(4, 'too short'), maxLength(5, 'too long')]),
  ]),
})

And I can simplify it like this:

const optionalFormString = (extraValidations: BaseValidation[] = []) => 
    union([
      EmptyStringToNullTransform,
      string([toTrimmed(), ...extraValidations]),
    ])

const form2 = object({
  firstname: optionalFormString(),
  lastname: optionalFormString([minLength(2, 'too short')]),
  zipCode: optionalFormString([
        minLength(4, 'too short'), 
        maxLength(5, 'too long')]),
})

What do you think about it?

@snrmwg
Copy link
Author

snrmwg commented Feb 27, 2024

As good as it looks. It could not make it work with Svelte Superforms.

In Zod it looks like this and it works:

const nullableFormString = (zs: ZodString = z.string()) => z.preprocess((v) => {
  if (typeof v === 'string') {
    const trimmed = v.trim()
    if (trimmed.length) return trimmed
  }
  return null
}, zs.nullable())

const SearchBoosterSchema = z.object({
  text: nullableFormString()
})

My Validbot schema looks like this:

export const SearchBoosterSchema = z.object({
  text: optionalFormString()
})

But then Superforms struggles with type problems "null vs. undefined". I can't see where source of undefined is.

@fabian-hiller
Copy link
Owner

You can to the same with Valibot. Check it out in our playground.

import * as v from 'valibot';

const Schema = v.coerce(
  v.nullable(v.string([v.minLength(3)])),
  (input) => (typeof input === 'string' && input.trim()) || null
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants