Front-end data validation begins with modeling

Simon Guo
Simon Guo
Aug 31, 2018 · 4 min read

What business problems do you think are the most annoying in the front-end development process?

I have been working on the front end for a long time. I don’t know if you have the same feelings as me. In some web applications, the form is more troublesome than other functional modules. It is a lot of physical activity, and it often takes time to verify the data.

In order to be able to make this part of the code more organized, we pre-define a data model through the Schema, throw the data in, and return the verification result.

Next, I introduce this tool. schema-typed is a data modeling and data validation tool. It can be used to design a form data structure. Of course, it is not limited to use in forms. If you use React in your product, the form components that work with React Suite are just as powerful.

Installation

npm install schema-typed --save

Usage

import { SchemaModel, StringType, DateType, NumberType } from 'schema-typed';const model = SchemaModel({
username: StringType().isRequired('Username required'),
email: StringType().isEmail('Email required'),
age: NumberType('Age should be a number').range(
18,
30,
'Age should be between 18 and 30 years old'
)
});
const checkResult = model.check({
username: 'foobar',
email: 'foo@bar.com',
age: 40
});
console.log(checkResult);

checkResult return structure is:

{
username: { hasError: false },
email: { hasError: false },
age: { hasError: true, errorMessage: 'Age should be between 18 and 30 years old' }
}

Multiple verification

StringType()
.minLength(6, "Can't be less than 6 characters")
.maxLength(30, 'Cannot be greater than 30 characters')
.isRequired('This field required');

Custom verification

Customize a rule with the addRule function.

If you are validating a string type of data, you can set a regular expression for custom validation by the pattern method.

const model = SchemaModel({
field1: StringType().addRule((value, data) => {
return /^[1-9][0-9]{3}\s?[a-zA-Z]{2}$/.test(value);
}, 'Please enter legal characters'),
field2: StringType().pattern(/^[1-9][0-9]{3}\s?[a-zA-Z]{2}$/, 'Please enter legal characters')
});
model.check({ field1: '', field2: '' });/**
{
field1: {
hasError: true,
errorMessage: 'Please enter legal characters'
},
field2: {
hasError: true,
errorMessage: 'Please enter legal characters'
}
};
**/

Custom verification — multi-field cross validation

E.g: verify that the two passwords are the same.

const model = SchemaModel({
password1: StringType().isRequired('This field required'),
password2: StringType().addRule((value, data) => {
if (value !== data.password1) {
return false;
}
return true;
}, 'The passwords are inconsistent twice')
});
model.check({ password1: '123456', password2: 'root' });/**
{
password1: { hasError: false },
password2: {
hasError: true,
errorMessage: 'The passwords are inconsistent twice'
}
};
**/

Validate nested objects

Validate nested objects, which can be defined using the ObjectType().shape method. E.g:

const model = SchemaModel({
id: NumberType().isRequired('This field required'),
name: StringType().isRequired('This field required'),
info: ObjectType().shape({
email: StringType().isEmail('Should be an email'),
age: numberType().min(18, 'Age should be greater than 18 years old')
});
});

It is more recommended to flatten the object.

import { flaser } from 'object-flaser';const model = SchemaModel({
id: NumberType().isRequired('This field required'),
name: StringType().isRequired('This field required'),
'info.email': StringType().isEmail('Should be an email'),
'info.age': numberType().min(18, 'Age should be greater than 18 years old')
});
const user = flaser({
id: 1,
name: 'schema-type',
info: {
email: 'schema-type@gmail.com',
age: 17
}
});
model.check(data);

API

StringType

  • isRequired()
StringType().isRequired('This field required');
  • isEmail(errorMessage: string)
StringType().isEmail('Please input the correct email address');
  • isURL(errorMessage: string)
StringType().isURL('Please enter the correct URL address');
  • isOneOf(items: Array, errorMessage: string)
StringType().isOneOf(['Javascript', 'CSS'], 'Can only type `Javascript` and `CSS`');
  • containsLetter(errorMessage: string)
StringType().containsLetter('Must contain English characters');
  • containsUppercaseLetter(errorMessage: string)
StringType().containsUppercaseLetter('Must contain uppercase English characters');
  • containsLowercaseLetter(errorMessage: string)
StringType().containsLowercaseLetter('Must contain lowercase English characters');
  • containsLetterOnly(errorMessage: string)
StringType().containsLetterOnly('English characters that can only be included');
  • containsNumber(errorMessage: string)
StringType().containsNumber('Must contain numbers');
  • pattern(regExp: RegExp, errorMessage: string)
StringType().pattern(/^[1-9][0-9]{3}\s?[a-zA-Z]{2}$/, 'Please enter legal characters');
  • rangeLength(minLength: number, maxLength: number, errorMessage: string)
StringType().rangeLength(6, 30, 'The number of characters can only be between 6 and 30');
  • minLength(minLength: number, errorMessage: string)
StringType().minLength(6, 'Minimum 6 characters required');
  • maxLength(maxLength: number, errorMessage: string)
StringType().minLength(30, 'The maximum is only 30 characters.');
  • addRule(onValid: Function, errorMessage: string)
StringType().addRule((value, data) => {
return /^[1-9][0-9]{3}\s?[a-zA-Z]{2}$/.test(value);
}, 'Please enter a legal character.');

NumberType

  • isRequired()
NumberType().isRequired('This field required');
  • isInteger(errorMessage: string)
NumberType().isInteger('It can only be an integer');
  • isOneOf(items: Array, errorMessage: string)
NumberType().isOneOf([5, 10, 15], 'Can only be `5`, `10`, `15`');
  • pattern(regExp: RegExp, errorMessage: string)
NumberType().pattern(/^[1-9][0-9]{3}$/, 'Please enter a legal character.');
  • range(minLength: number, maxLength: number, errorMessage: string)
NumberType().range(18, 40, 'Please enter a number between 18 - 40');
  • min(min: number, errorMessage: string)
NumberType().min(18, 'Minimum 18');
  • max(max: number, errorMessage: string)
NumberType().max(40, 'Maximum 40');
  • addRule(onValid: Function, errorMessage: string)
NumberType().addRule((value, data) => {
return value % 5 === 0;
}, 'Please enter a valid number');

ArrayType

  • isRequired()
ArrayType().isRequired('This field required');
  • rangeLength(minLength: number, maxLength: number, errorMessage: string)
ArrayType().rangeLength(1, 3, 'Choose at least one, but no more than three');
  • minLength(minLength: number, errorMessage: string)
ArrayType().minLength(1, 'Choose at least one');
  • maxLength(maxLength: number, errorMessage: string)
ArrayType().maxLength(3, "Can't exceed three");
  • unrepeatable(errorMessage: string)
ArrayType().unrepeatable('Duplicate options cannot appear');
  • of(type: Object, errorMessage: string)
ArrayType().of(StringType().isEmail(), 'wrong format');
  • addRule(onValid: Function, errorMessage: string)
ArrayType().addRule((value, data) => {
return value.length % 2 === 0;
}, 'Good things are in pairs');

DateType

  • isRequired()
DateType().isRequired('This field required');
  • range(min: Date, max: Date, errorMessage: string)
DateType().range(
new Date('08/01/2017'),
new Date('08/30/2017'),
'Date should be between 08/01/2017 - 08/30/2017'
);
  • min(min: Date, errorMessage: string)
DateType().min(new Date('08/01/2017'), 'Minimum date 08/01/2017');
  • max(max: Date, errorMessage: string)
DateType().max(new Date('08/30/2017'), 'Maximum date 08/30/2017');
  • addRule(onValid: Function, errorMessage: string)
DateType().addRule((value, data) => {
return value.getDay() === 2;
}, 'Can only choose Tuesday');

ObjectType

  • isRequired()
ObjectType().isRequired('This field required');
  • shape(type: Object)
ObjectType().shape({
email: StringType().isEmail('Should be an email'),
age: NumberType().min(18, 'Age should be greater than 18 years old')
});
  • addRule(onValid: Function, errorMessage: string)
ObjectType().addRule((value, data) => {
if (value.id || value.email) {
return true;
}
return false;
}, 'Id and email must have one that cannot be empty');

BooleanType

  • isRequired()
BooleanType().isRequired('This field required');
  • addRule(onValid: Function, errorMessage: string)
ObjectType().addRule((value, data) => {
if (typeof value === 'undefined' && A === 10) {
return false;
}
return true;
}, 'This value is required when A is equal to 10');

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade