Constraint validation

Custom validation classes

If you have custom validation logic you can create a Constraint class:


  1. First create a file, lets say , and define a new class:

    import{ValidatorConstraint,ValidatorConstraintInterface,ValidationArguments}from"class-validator";@ValidatorConstraint({ name"customText", asyncfalse})exportclassCustomTextLengthimplements ValidatorConstraintInterface {validate(textstring,argsValidationArguments){returntext.length>1&&text.length<10;}defaultMessage(argsValidationArguments){return"Text ($value) is too short or too long!";}}

    We marked our class with decorator. You can also supply a validation constraint name — this name will be used as «error type» in ValidationError. If you will not supply a constraint name — it will be auto-generated.

    Our class must implement interface and its method, which defines validation logic. If validation succeeds, method returns true, otherwise false. Custom validator can be asynchronous, if you want to perform validation after some asynchronous operations, simply return a promise with boolean inside in method.

    Also we defined optional method which defines a default error message, in the case that the decorator’s implementation doesn’t set an error message.

  2. Then you can use your new validation constraint in your class:

    import{Validate}from"class-validator";import{CustomTextLength}from"./CustomTextLength";exportclassPost{    @Validate(CustomTextLength,{        message"Title is too short or long!"})    title string;}

    Here we set our newly created validation constraint for .

  3. And use validator as usual:

    import{validate}from"class-validator";validate(post).then(errors=>{});

You can also pass constraints to your validator, like this:

import{Validate}from"class-validator";import{CustomTextLength}from"./CustomTextLength";exportclassPost{    @Validate(CustomTextLength,3,20,{        message"Wrong post title"})    title string;}

And use them from object:

import{ValidationArguments,ValidatorConstraint,ValidatorConstraintInterface}from"class-validator";@ValidatorConstraint()exportclassCustomTextLengthimplements ValidatorConstraintInterface {validate(textstring,validationArgumentsValidationArguments){returntext.length>validationArguments.constraints&&text.length<validationArguments.constraints1;}}

Validation errors

The method returns an array of objects. Each is:

{    targetObject;    property string;    value any;    constraints?{type string string;};    children? ValidationError;}

In our case, when we validated a Post object, we have such an array of objects:

{    target,    property"title",    value"Hello",    constraints{        length"$property must be longer than or equal to 10 characters"}},{    target,    property"text",    value"this is a great post about hell world",    constraints{        contains"text must contain a hello string"}},

If you don’t want a to be exposed in validation errors, there is a special option when you use validator:

validator.validate(post,{ validationError{ targetfalse}});

This is especially useful when you send errors back over http, and you most probably don’t want to expose the whole target object.

Validation messages

You can specify validation message in the decorator options and that message will be returned in the returned by the method (in the case that validation for this field fails).

import{MinLength,MaxLength}from"class-validator";exportclassPost{    @MinLength(10,{        message"Title is too short"})    @MaxLength(50,{        message"Title is too long"})    title string;}

There are few special tokens you can use in your messages:

  • — the value that is being validated
  • — name of the object’s property being validated
  • — name of the object’s class being validated
  • , , … — constraints defined by specific validation type

Example of usage:

import{MinLength,MaxLength}from"class-validator";exportclassPost{    @MinLength(10,{        message"Title is too short. Minimal length is $constraint1 characters, but actual is $value"})    @MaxLength(50,{        message"Title is too long. Maximal length is $constraint1 characters, but actual is $value"})    title string;}

Also you can provide a function, that returns a message. This allows you to create more granular messages:

import{MinLength,MaxLength,ValidationArguments}from"class-validator";exportclassPost{    @MinLength(10,{message(argsValidationArguments)=>{if(args.value.length===1){return"Too short, minimum length is 1 character";}else{return"Too short, minimum length is "+args.constraints+" characters";}}})    title string;}

Message function accepts which contains the following information:

  • — the value that is being validated
  • — array of constraints defined by specific validation type
  • — name of the object’s class being validated
  • — object that is being validated
  • — name of the object’s property being validated

Inheriting Validation decorators

When you define a subclass which extends from another one, the subclass will automatically inherit the parent’s decorators. If a property is redefined in the descendant class decorators will be applied on it both from that and the base class.

import{validate}from"class-validator";classBaseContent{    @IsEmail()    email string;    @IsString()    password string;}classUserextendsBaseContent{    @MinLength(10)    @MaxLength(20)    name string;    @Contains("hello")    welcome string;    @MinLength(20)    password string;}let user =newUser();user.email="invalid email";user.password="too short"user.name="not valid";user.welcome="helo";validate(user).then(errors=>{});

link Refactoring rules


Whenever you have multiple fields with the same rules and messages, refactoring those can reduce a lot of duplication. Using addMethod and addClassRules are most effective for that.

Let’s consider an example where you have ten customer fields, each required and with a minlength of 2. You need custom messages for both rules. To avoid having to specify those rules and messages again and again, we can alias existing methods with different messages and group them into a single class:

1 2 3 4 5 6 7 8 9

With that in place, we can add a class customer to all customer fields and be done with it:

1 2 3
1

Error messages

An error message displays a hint for the user about invalid elements, and what is wrong. There are four ways to provide error messages. Via the title attribute of the input element to validate, via data attributes, via error labels and via plugin settings (option ).

All validation rules included here provide a default error message which you can use for prototyping, because it is used when no specific message is provided.

The priorities are as follows: A custom message (passed by plugin options), the element’s title, the default message.

When using data attributes, you can set a generic message for all rules, or specific messages per rule:

1 2

Error message display

Error messages are handled via label elements with an additional class (option ). The link between the message and the invalid element is provided via the labels for attribute. When provided in the markup, they are shown and hidden accordingly, and otherwise created on demand. By default, labels are created after the invalid element, this is also customizable (option ). It is also possible to put them into an error container (option ). To use a different element then a label, specify the option.

General messages

In addition to field-specific messages you can display a general «your form is invalid, please fix the highlighted fields!» message in a container anywhere on your page, eg. above the form (option ). The container is shown and hidden when errors occur and are fixed accordingly. The container for error labels (option ) can also be nested inside the error container.

Focusing of invalid elements

By default, the first invalid element in a form is focused after submitting a form with invalid elements. To prevent confusion on the behalf of the user, the plugin remembers the element that had focus when the form was submitted, and refocuses that element. That way the user can try to fill out elements of the form at the end, without being forced to focus them again and again. This can be disabled (option ).

Form submit

By default, the form submission is prevented when the form is invalid, and submitted as normal when it is valid. You can also handle the submission manually (option ).

JS Уроки

JS HOMEJS IntroductionJS Where ToJS OutputJS StatementsJS SyntaxJS CommentsJS VariablesJS OperatorsJS ArithmeticJS AssignmentJS Data TypesJS FunctionsJS ObjectsJS ScopeJS EventsJS StringsJS String MethodsJS NumbersJS Number MethodsJS ArraysJS Array MethodsJS Array SortJS Array IterationJS DatesJS Date FormatsJS Date Get MethodsJS Date Set MethodsJS MathJS RandomJS BooleansJS ComparisonsJS ConditionsJS SwitchJS Loop ForJS Loop WhileJS BreakJS Type ConversionJS BitwiseJS RegExpJS ErrorsJS DebuggingJS HoistingJS Strict ModeJS this KeywordJS Style GuideJS Best PracticesJS MistakesJS PerformanceJS Reserved WordsJS VersionsJS Version ES5JS Version ES6JS JSON

Usage

Basic usage involves defining a descriptor, assigning it to a schema and passing the object to be validated and a callback function to the method of the schema:

import Schema from 'async-validator';
const descriptor = {
  name: {
    type: 'string',
    required: true,
    validator: (rule, value) => value === 'muji',
  },
  age: {
    type: 'number',
    asyncValidator: (rule, value) => {
      return new Promise((resolve, reject) => {
        if (value < 18) {
          reject('too young');  // reject with error message
        } else {
          resolve();
        }
      });
    },
  },
};
const validator = new Schema(descriptor);
validator.validate({ name: 'muji' }, (errors, fields) => {
  if (errors) {
    // validation failed, errors is an array of all errors
    // fields is an object keyed by field name with an array of
    // errors per field
    return handleErrors(errors, fields);
  }
  // validation passed
});

// PROMISE USAGE
validator.validate({ name: 'muji', age: 16 }).then(() => {
  // validation passed or without error message
}).catch(({ errors, fields }) => {
  return handleErrors(errors, fields);
});

Расширения Chrome для проверки HTML

Расширения Chrome предлагают отличный способ расширить ваш браузер с помощью инструментов, которые помогут вам в вашей повседневной работе веб-разработчика. Здесь представлены самые популярные расширения, которые помогут вам с проверкой HTML в Chrome.

Web Developer

Это расширение является обязательным для всех веб-разработчиков, поскольку оно предлагает хороший набор инструментов, которые помогут вам в вашей работе: отключение JavaScript, управление файлами cookie, настройка CSS, формы … а также ссылки для проверки текущей страницы в W3C Validator.

Расширение веб-разработчика предоставляет вам ссылки для проверки HTML (как с помощью URL-адреса, так и путем отправки локального HTML-кода в качестве текстового ввода), CSS, каналов, доступности на волне и проверки наличия неработающих ссылок.


Плюсы: приятно интегрированы с другими инструментами веб-разработки, простой интерфейс.

Минусы: не могут быть настроены, вы не можете использовать свой собственный экземпляр валидатора или выполнять проверки автоматически на определенных хостах, используется устаревший валидатор.

Validity

Расширение Validity позволяет проверять HTML в устаревшем W3C Validator и отображает проблемы, обнаруженные в консоли JavaScript, вместо открытия новой вкладки. Показывает только строку, в которой находится проблема, и общее описание – без начальных и конечных строк и столбцов, без выдержек и ссылок для дополнительной помощи.

Он также может быть немного запутанным, поскольку выходные данные смешиваются с другими предупреждениями JS и журналами, которые выводятся в вашей консоли JS. Вероятно, было бы более читабельным, если бы вывод находился на отдельной вкладке, а не в консоли JS.

Плюсы: может быть настроен на использование собственного экземпляра валидатора и может автоматически проверяться на определенных хостах.

Минусы: Зависит от действующего валидатора, не может напрямую использовать Nu Validator. Вывод ограничен и грязен, поскольку использует консоль JavaScript.

HTML Validation Bookmarklet

Acid.JS Validator – это бесплатный букмарклет, который использует API синтаксического анализатора W3C SGML для проверки разметки страницы, на которой он вызывается.

Другие расширения которые не работали на момент написания статьи

  • W3C Validator . Не является официальным расширением W3C, оно должно использовать устаревший API валидатора и отображать проблемы на консоли JS, но оно не работает.
  • HTML валидатор . Это не работает, вероятно, потому что у него была опция автозапуска, которая могла привести к его запрету .
  • Kingsquare HTML Validator . Предполагается, что он предлагает альтернативный способ проверки HTML с использованием библиотеки Tidy HTML вместо W3C Validator, он просто не работает.

Контрибьютерам

А теперь сюрприз. Самих assert’ов в библиотеке ещё практически и нет. Только парочка базовых вроде , , и ещё немного для примеров. Нету даже тех, которые я использовал в листингах этого поста

Всё дело в том, что мне хотелось привлечь внимание к идее, а не к реализации

Более того, я считаю, что моё видение необходимых assert’ов может сильно отличаться от видения сообщества. И, делая этот инструмент «под себя» я могу сделать его неудобным для других JS разработчиков. И у меня возникла идея — привлечь к созданию этого инструмента JS сообщество. Что бы самому ничего не делать он покрывал потребности сообщества, а не мои собственные.

Я приглашаю JS разработчиков стать контрибьютерами validate.it.js. Всех кто хочет контрибьютить в опенсорс, но не знает с какого проекта начать. Всех кто хочет сделать для себя и для всего сообщества удобный инструмент валидации.

Вместе мы сможем наполнить коллекцию реально необходимыми всем нам assert’ами.

При этом контрибьютером может быть разработчик любого уровня. Ведь кто-то захочет заняться валидацией по длине строки, а кому-то, например, интереснее реализовывать проверку на соответствие даты всем вариантам стандарта ISO 8601.

К pull-request’ам предъявляются весьма мягкие требования:

  • наличие тестов
  • наличие описания — что бы вставить его в раздел с перечнем assert’ов.

Usage

Create your class and put some validation decorators on the properties you want to validate:

import{validate,validateOrReject,Contains,IsInt,Length,IsEmail,IsFQDN,IsDate,Min,Max}from"class-validator";exportclassPost{    @Length(10,20)    title string;    @Contains("hello")    text string;    @IsInt()    @Min()    @Max(10)    rating number;    @IsEmail()    email string;    @IsFQDN()    site string;    @IsDate()    createDateDate;}let post =newPost();post.title="Hello";post.text="this is a great post about hell world";post.rating=11;post.email="google.com";post.site="googlecom";validate(post).then(errors=>{if(errors.length>){console.log("validation failed. errors: ", errors);}else{console.log("validation succeed");}});validateOrReject(post).catch(errors=>{console.log("Promise rejected (validation failed). Errors: ", errors);});asyncfunctionvalidateOrRejectExample(input){try{awaitvalidateOrReject(input);}catch(errors){console.log("Caught promise rejection (validation failed). Errors: ", errors)}}

The function optionally expects a object as a second parameter:

exportinterfaceValidatorOptions{skipMissingProperties?boolean;whitelist?boolean;forbidNonWhitelisted?boolean;groups?string;dismissDefaultMessages?boolean;validationError?{target?boolean;value?boolean;};forbidUnknownValues?boolean;}

Sanitizers

Here is a list of the sanitizers currently available.

Sanitizer Description
blacklist(input, chars) remove characters that appear in the blacklist. The characters are used in a RegExp and so you will need to escape some chars, e.g. .
escape(input) replace , , , , and with HTML entities.
ltrim(input ) trim characters from the left-side of the input.
normalizeEmail(email ) canonicalizes an email address. (This doesn’t validate that the input is an email, if you want to validate the email use isEmail beforehand) is an object with the following keys and default values:
  • all_lowercase: true — Transforms the local part (before the @ symbol) of all email addresses to lowercase. Please note that this may violate RFC 5321, which gives providers the possibility to treat the local part of email addresses in a case sensitive way (although in practice most — yet not all — providers don’t). The domain part of the email address is always lowercased, as it’s case insensitive per RFC 1035.
  • gmail_lowercase: true — GMail addresses are known to be case-insensitive, so this switch allows lowercasing them even when all_lowercase is set to false. Please note that when all_lowercase is true, GMail addresses are lowercased regardless of the value of this setting.
  • gmail_remove_dots: true: Removes dots from the local part of the email address, as GMail ignores them (e.g. «john.doe» and «johndoe» are considered equal).
  • gmail_convert_googlemaildotcom: true: Converts addresses with domain @googlemail.com to @gmail.com, as they’re equivalent.
  • outlookdotcom_lowercase: true — Outlook.com addresses (including Windows Live and Hotmail) are known to be case-insensitive, so this switch allows lowercasing them even when all_lowercase is set to false. Please note that when all_lowercase is true, Outlook.com addresses are lowercased regardless of the value of this setting.
  • yahoo_lowercase: true — Yahoo Mail addresses are known to be case-insensitive, so this switch allows lowercasing them even when all_lowercase is set to false. Please note that when all_lowercase is true, Yahoo Mail addresses are lowercased regardless of the value of this setting.
  • icloud_lowercase: true — iCloud addresses (including MobileMe) are known to be case-insensitive, so this switch allows lowercasing them even when all_lowercase is set to false. Please note that when all_lowercase is true, iCloud addresses are lowercased regardless of the value of this setting.
rtrim(input ) trim characters from the right-side of the input.
stripLow(input ) remove characters with a numerical value < 32 and 127, mostly control characters. If is , newline characters are preserved ( and , hex and ). Unicode-safe in JavaScript.
toBoolean(input ) convert the input string to a boolean. Everything except for , and returns . In strict mode only and return .
toDate(input) convert the input string to a date, or if the input is not a date.
toFloat(input) convert the input string to a float, or if the input is not a float.
toInt(input ) convert the input string to an integer, or if the input is not an integer.
trim(input ) trim characters (whitespace by default) from both sides of the input.
unescape(input) replaces HTML encoded entities with , , , , and .
whitelist(input, chars) remove characters that do not appear in the whitelist. The characters are used in a RegExp and so you will need to escape some chars, e.g. .

API


Js Form Validation has 2 methods only:

set your validation rules to your form

formName Type: String your html form name.

rules Type: Object An object with validation rules. (See Config Object section)

formValidator.setFormValidation('userRegistrationForm', registrationFormRules);

add custom form validator

validatorName Type: String name of your validator (example: ‘passwordConfirmation’).

callback Type: Function your validator function.

Notice:

your validator function should has the following structure:

functionvalidator(fieldValue,data,firstBindedFoueldValue,secondBoundedFieldValue,....){}

where:

  • fieldValue — current validated field value
  • data — additional data provided by config object
  • firstBoundedFieldValue, secondBoundedFieldValue — values of bounded field (example: password field in password confirmation check)

if you didn’t specify ‘data’ in config object, you still must provide data parameter in your function!

Example:

our favorite password confirmation validator 🙂

     formValidator.addValidator('confirmPassword',function(passwordConfirmation,data,originalPassword){return passwordConfirmation === originalPassword});

vee-validate

vee-validate — библиотека для валидаций, появившаяся во времена Vue.js первой версии, имеет большое сообщество и используется в большом количестве проектов.

Возраст сказывается на весе — 31 КБайт (minified+GZIP), в полтора раза больше самого Vue.js! Связано это с тем, что библиотека содержит в себе сразу кучу вещей:

  • 35 встроенных валидаторов
  • Перевод ошибок на английский язык
  • Директивы для валидаций
  • Компоненты для валидаций

Библиотека поддерживает 41 язык для ошибок. Чтобы установить и использовать ее с нужной локализацией, требуется выполнить всего пару шагов:

У vee-validate есть два подхода к валидациям: с помощью директивы и с помощью компонентов.

Валидация с помощью директивы v-validate

Подход с помощью директивы очень прост: вы вешаете на поле ввода директиву v-validate, которой передаете список валидаторов. Состояние валидации и строчки ошибок затем можно получить из полей fields и errors внутри компонента.

Чтобы посмотреть ее использование на примере, давайте сделаем простую форму, которая:

— Содержит поле «Серия и номер паспорта» — Содержит поле «Дата выдачи паспорта» — Содержит поле «Имя» — Добавляет атрибут disabled на кнопку отправки формы если данные неверны

Валидации с помощью компонентов

Новый способ валидации, который появился в конце прошлого года — использование компонентов. Сами авторы рекомендуют использовать этот подход, и он отлично сочетается с новым синтаксисом слотов из Vue.js@2.6.

Библиотека предоставляет два компонента:

  • ValidationProvider — компонент, в котором вы описываете валидации и в который оборачиваете поле ввода.
  • ValidationObserver — компонент, который объединяет флаги валидаций нескольких ValidationProvider’ов.

Вот форма из прошлого примера, но написанная с помощью компонентов:

Проблемы

Первая и самая большая проблема — это, конечно же, ее размер. Библиотека не поддерживает tree-shaking и выборочное добавление валидаторов. Хотите вы или нет, в вашем банде всегда будет присутствовать 2 компонента и директива валидаций, перевод на английский язык и 35 валидаторов.

Вторая проблема — из-за подхода, основанного на подписке на события, могут возникать проблемы при интеграции с другими библиотеками, которые тоже изменяют поведение полей ввода (маскеры и т.п.).

Третья проблема — более субъективная — переводы ошибок общие и некрасивые, не отражают реальной сути.

Возьмем форму из прошлых примеров. Если вы введете неправильный номер и дату паспорта, то получите такие ошибки:

Хочется заменить их на что-то более удобочитаемое:


С этим читают