Skip to content
This repository has been archived by the owner on Feb 13, 2021. It is now read-only.

Examples

Prescott Breeden edited this page Nov 19, 2020 · 7 revisions

Basic Examples

More Examples


Codependent Validation

There are times when two or more fields on a form are inextricably linked to one another, but generating separate validation messages could lead to confusion. For example, say you have a validation that is dependent on another field that stipulates if Back to the Future is selected from one list, that the only acceptable rating is 10/10. In this situation, a user could select a rating first, then select the movie, and the validation wouldn't trigger until after the form submission. For a small form this might be an accepted cost, but if it is a long form, it would be nice to let the user know right away that they have entered an invalid state.

Codependent Validation is easily accomplished by creating a function that stipulates which properties of the schema should execute with validateAll. See "validateTogether" below:

import React, { useState, FC } from 'react';
import { Dog } from '...';
import { DogValidation } from '...';

export const Example2: FC = () => {

  const [state, setState] = useState<Dog>({
    name: '',
    breed: '',
  });

  const v = DogValidation();

  const validateTogether = (name: string, data: any) => {
    const properties = ['name', 'breed'];
    properties.includes(name) && v.validateAll(data, properties);
  };

  const handleChange = (event: any) => {
    { ... }
    validateTogether(name, updatedState);
  };

  const handleBlur = (event: any) => {
    const { name } = event.target;
    validateTogether(name, state);
  };

  const handleSubmit = (e: any) => { ... };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>Name</label>
        <input
          name="name"
          onBlur={handleBlur}
          onChange={handleChange}
          value={state.name}
        />
        {v.getError('name') && <p>{v.getError('name')}</p>}
      </div>
      <div>
        <label>Breed</label>
        <input
          name="breed"
          onBlur={handleBlur}
          onChange={handleChange}
          value={state.breed}
        />
        {v.getError('breed') && <p>{v.getError('breed')}</p>}
      </div>
      <button>Submit</button>
    </form>
  );
};

Nested Forms

Nesting is not uncommon when handling dynamic forms where a user can add as many inputs as they need. Here is a code sandbox with multiple nested forms but the basic idea is sketched out below.

Nested Phone Validation Schema

import { useValidation } from 'de-formed-validations';
import { replace } from 'ramda';
import { Phone } from 'types/phone.type';
import { compose } from 'utilities/general.utils';
import {
  isLength,
  stringIsNotEmpty,
  stringIsNumbers,
} from 'utilities/validation.utils';

// PhoneValidation :: () -> ValidationObject<Phone>
export const PhoneValidation = () => {
  return useValidation<Phone>({
    number: [
      {
        errorMessage: 'Number is required.',
        validation: stringIsNotEmpty,
      },
      {
        errorMessage: 'Can only have digits.',
        validation: compose(
          stringIsNumbers,
          replace(/[-.() ]/g, '')
        ),
      },
      {
        errorMessage: 'Must be 10 digits.',
        validation: compose(
          isLength(10),
          replace(/[-.() ]/g, '')
        ),
      },
    ],
    description: [
      {
        errorMessage: 'Description is required.',
        validation: stringIsNotEmpty,
      },
    ],
  });
};

Nested Pet Validation Schema

import { useValidation } from 'de-formed-validations';
import { Pet } from 'types/pet.type';
import { isCat, isDog } from 'utilities/pet.utils';
import { matchString, stringIsNotEmpty } from 'utilities/validation.utils';

// PetValidation :: () -> ValidationObject<Pet>
export const PetValidation = () => {
  return useValidation<Pet>({
    name: [
      {
        errorMessage: 'Name is required.',
        validation: stringIsNotEmpty,
      },
    ],
    breed: [
      {
        errorMessage: 'Breed is required.',
        validation: stringIsNotEmpty,
      },
      {
        errorMessage: 'Garfield is a Persian/Tabby',
        validation: (val: string, pet: Pet) => {
          if (isCat(pet)) {
            return matchString(pet.name, 'Garfield')
              ? matchString(val, 'Persian/Tabby')
              : true;
          }
          return true;
        },
      },
    ],
    favoriteChewToy: [
      {
        errorMessage: 'Favorite Chew Toy is required.',
        validation: (val: string, pet: Pet) => {
          return isDog(pet) ? stringIsNotEmpty(val) : true;
        },
      },
    ],
    sleepingHabits: [
      {
        errorMessage: 'Sleeping Habits is required.',
        validation: (val: string, pet: Pet) => {
          return isCat(pet) ? stringIsNotEmpty(val) : true;
        },
      },
    ],
  });
};

Contact Validation:

import { useValidation } from 'de-formed-validations';
import { map } from 'ramda';
import { Contact } from 'types/contact.type';
import { Pet } from 'types/pet.type';
import { all, compose } from 'utilities/general.utils';
import {
  containsNoNumbers,
  emailIsValid,
  stringIsLessThan,
  stringIsNotEmpty,
} from 'utilities/validation.utils';
import { PetValidation } from './pet.validation';
import { PhoneValidation } from './phone.validation';

// ContactValidation :: () -> ValidationObject<Contact>
export const ContactValidation = () => {
  const { validateAll: validatePhone } = PhoneValidation();
  const { validateAll: validatePet } = PetValidation();
  return useValidation<Contact>({
    name: [
      {
        errorMessage: 'Name is required.',
        validation: stringIsNotEmpty,
      },
      {
        errorMessage: 'Name cannot contain numbers.',
        validation: containsNoNumbers,
      },
      {
        errorMessage: 'Name must be less than 40 characters.',
        validation: stringIsLessThan(40),
      },
    ],
    subscriptionEmail: [
      {
        errorMessage: 'Please provide an email.',
        validation: (val: string, contact: Contact) => {
          return contact.isSubcribed ? stringIsNotEmpty(val) : true;
        },
      },
      {
        errorMessage: 'Email is invalid.',
        validation: (val: string, contact: Contact) => {
          return contact.isSubcribed ? emailIsValid(val) : true;
        },
      },
    ],
    phones: [
      {
        errorMessage: 'Not all phones provided are valid.',
        validation: compose(
          all,
          map(validatePhone)
        ),
      },
    ],
    dog: [
      {
        errorMessage: 'Must be a valid dog.',
        validation: (val: Pet) => validatePet(val),
      },
    ],
    cat: [
      {
        errorMessage: 'Must be a valid cat.',
        validation: (val: Pet) => validatePet(val),
      },
    ],
  });
};