Skip to content

Conversation

@lakardion
Copy link
Contributor

@lakardion lakardion commented Oct 31, 2024

PROTOTYPE

This is an attempt to make this library typesafe.

But at the same time it is likely that we will need to rewrite a part of it.

I'm trying to keep as much as possible the existing code but it is likely that some things might need to change:

  • Maybe validators are to be revisited due to the lack of type inference we can get from them. I think our best bet for this is just using zods. This will provide the right type inference for the form value once it is validated ( no more form.email.value ?? '' )

Roadmap

  • basic form builder with just FormField and typesafety
    • addField
    • validate
    • value
    • addFormLevelValidator
    • replicate
    • errors
    • isValid
    • addValidator
  • Create type assertion on validate so that form.value becomes the validated value of the form (required fields would show as defined)
  • Add support for FormArray
  • 🚧 ...

How would this look in practice

import { createForm } from './builder'
import {
  EmailValidator,
  MinLengthValidator,
  MustMatchValidator,
  RequiredValidator,
} from './validators'

export const accountForm = createForm()
  .addField({
    name: 'firstName',
    label: 'First name',
    placeholder: 'First Name',
    type: 'text',
    validators: [new RequiredValidator({ message: 'Please enter your first' })],
    value: '',
  })
  .addField({
    name: 'lastName',
    label: 'Last Name',
    placeholder: 'Last Name',
    type: 'text',
    validators: [new RequiredValidator({ message: 'Please enter your last name' })],
    value: '',
  })
  .addField({
    name: 'email',
    label: 'Email',
    placeholder: 'Email',
    type: 'email',
    value: '',
    validators: [new EmailValidator({ message: 'Please enter a valid email' })],
  })
  .addField({
    name: 'password',
    label: 'Password',
    placeholder: 'Password',
    type: 'password',
    validators: [
      new MinLengthValidator({
        minLength: 8,
        message: 'Please enter a password with a minimum of 8 characters',
      }),
    ],
    value: '',
  })
  .addField({
    name: 'confirmPassword',
    label: 'Confirm Password',
    placeholder: 'Confirm Password',
    type: 'password',
    value: '',
    validators: [],
  })
  .addFormLevelValidator(
    'confirmPassword',
    new MustMatchValidator({
      message: 'Passwords must match',
      matcher: 'password',
    }),
  )

‼️ Note that:

  • No more static fields
  • No need to FormField.create (it's wrapped in addField
  • We work with the instance of the class (not sure yet whether this will actually pose a problem but will see as development goes. Alternative would be to just wrap this in a function call )
  • No need to declare many type helpers to get the right types for the form. The types should be all inferred from this declaration.

Comment on lines +44 to +51
validate() {
for (const f of Object.values(this.fields)) {
if (f instanceof FormField) {
f.validate()
}
}
return this
}
Copy link
Contributor Author

@lakardion lakardion Nov 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should break out of the builder pattern and instead return a success prop which would tell whether the validation was successful.
If the validation was successful we should also return a data or value where the fully validated form value is returned, so that we can use that type-safely

@oudeismetis From our discussion on bootstrapper

@paribaker
Copy link
Collaborator

paribaker commented Nov 22, 2024

Does this resolve the issue with the values allowing for undefined? I was actually thinking we can reduce the number of types that are needed by adding a type to the FormField (and removing the useless one from the Form). From there using some of the typescript type functions to return the value type without undefined IF the field has the required validator otherwise leaving it with it.
I was also thinking that creating a useForm method for Vue like you have for react, could infer the type of the form instead.

const {formInstance, validateField, addField, translate etc} = useForm(FormClass)

@lakardion
Copy link
Contributor Author

lakardion commented Nov 22, 2024

The main issue has always been the static fields. We cannot do any inferring with static types bc they're at class level

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants