# govuk-eva-validation

> Eva validation system

Latest version **2.3.4** (published 2021-09-06) · ISC license · 0 weekly downloads

## Install

```sh
npm install govuk-eva-validation
pnpm add govuk-eva-validation
yarn add govuk-eva-validation
bun add govuk-eva-validation
```

## Health

**Score 25/100 (F)** — status: abandoned.

Positive: has types; no vulnerabilities; high quality score.

Warnings: low downloads; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 2.3.4 |
| Published | 2021-09-06 |
| First published | 2020-11-04 |
| Weekly downloads | 0 |
| License | ISC |
| TypeScript types | bundled |
| Module format | CommonJS |
| Dependencies | 2 |
| Unpacked size | 91.1 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | Anthony Griffiths |
| Maintainers | griffgruff |

## Links

- npm: https://www.npmjs.com/package/govuk-eva-validation
- Repository: git@gitlab.com:dwp/searchlight/govuk-eva
- npm.io page: https://npm.io/package/govuk-eva-validation

## Dependencies (2)

- [dayjs](https://npm.io/package/dayjs.md) ^1.9.6
- [lodash](https://npm.io/package/lodash.md) ^4.17.21

## Recent versions

- 2.3.4 (latest) — 2021-09-06
- 2.3.3 — 2021-09-06
- 2.2.0 — 2021-07-26
- 2.1.1 — 2021-03-24
- 2.0.0 — 2021-01-22
- 1.3.13-beta — 2021-01-12
- 1.3.12-beta — 2021-01-11
- 1.3.11-beta — 2021-01-11
- 1.3.10-beta — 2021-01-11
- 1.3.9-beta — 2021-01-11
- 1.3.8-beta — 2021-01-11
- 1.3.7-beta — 2021-01-10
- 1.3.6-beta — 2021-01-10
- 1.3.5-beta — 2021-01-10
- 1.3.5 — 2020-12-18
- … 26 more at https://npm.io/package/govuk-eva-validation/versions

## README

![logo](./docs/eva-logo.png)


[[_TOC_]]

# What is Eva?

It's a tiny little validation library! Eva aims to be a simple and **light** fluent validation library for use within our Angular web-components (govuk-angular). The API is readable and allows for easy creation of complex validations.

## Current status

Very much in development! :-)

## Install

>npm i govuk-eva-validation

## Not another validation library!

When working on an application using the gov.uk design system and Angular or any SPA application, we have a **requirement** for a validation system, a system that can be used to check our models and return a validation results. Results which can then be used by our web-components to show validation errors to our customers. Those error messages and how they are displayed must conform to the patterns outlined by the gov.uk design system.

With the above requirements our options are limited.

* Write our own tiny validation system.
* Use one of the many validation libraries ( Joi, Yup, v8n.. and others)

So we wrote our own!!! :-) see below for details. Out of all the options we had this was the simplest way to meet our validation requirements. The other libraries above would have needed adapting to work with our web-components, that would have been a fair bit of work.

## Why is it named Eva?

Eva is just an abbreviation of evaluate, as in evaluate this model for errors.

## Features

* Fluent and chainable API.
* Useful standard validation rules.
* Allows for custom validation messages.
* Allows for flexible validation results.
* Works in conjunction with the govuk-angular component library

The source code and specs files provide a great source of information on the library. Dig in to find more examples.

# Schema Validation

If you just want to validate a model, then the example below is a good start. When creating a schema, the property names must match your model property names. This example uses the string validation.

```javascript
  const model = {
    surname : "",
    forename: "smith",
  }

  // create a schema for that model
  const schema: EvaSchema = {
    surname : eva()
              .string()
              .pattern(/^[A-Z][a-z]*/)
              .required(),

    forename : eva()
                .string()
                .pattern(/^[A-Z]*/)
                .required()
  };

  // Execute the validation
  const result = eva().validate(model, schema);
```

### What about the validation result? 

After executing the **validate** function, the result type is the "model-state".

The result is shown below, there is : 

* The **errorSummary** which hold each ValidationResult
* For each **property** a validation result

```json
 {
      "hasBeenValidated": true,
      "hasError": true,
      "errorSummary": {
        "hasError": true,
        "errors": [
          {
            "id": "surname",
            "hasError": true,
            "fullError": "Surname is not a valid format",
            "shortError": "Invalid format"
          },
          {
            "id": "forename",
            "hasError": true,
            "fullError": "Forename is not a valid format",
            "shortError": "Invalid format"
          }
        ]
      },
      "surname": {
        "id": "surname",
        "hasError": true,
        "fullError": "Surname is not a valid format",
        "shortError": "Invalid format"
      },
      "forename": {
        "id": "forename",
        "hasError": true,
        "fullError": "Forename is not a valid format",
        "shortError": "Invalid format"
      }
    }
```

# Validation Chain

Eva allows you to chain together as many validators as you want. When the chain of validators is executed, eva will stop when one of them returns an error. If all of the validators pass, then the validation is succesful. 

There is an exception, sometimes you might want to stop the chain from running if a condition is true. See the **validWhenEmpty** validator


# ValidationResult

A **validationResults** consists of : 

* **id**, normally the property name
* **hasError**, error indicator
* **fullError**, full error using the property name and a standard error message
* **shortError**, short error, without the property name

## Overriding the ValidationResult label

By default the **fullError** message contains the **property name** from the schema. but you might what to change that. It's fairly easy, in this example we want the error message to contain **Customer Surname** and not just **Surname**. This can be acheived by passing in a **label** value, see below.

```javascript
  // create a schema for that model
  const schema: EvaSchema = {
    surname : eva({label : "Customer Surname"})
              .string()
              .required(),
  };
```
```json
  "surname": {
    "id": "surname",
    "hasError": true,
    "fullError": "Customer Surname is not a valid format",
    "shortError": "Invalid format"
  },
```
## Overriding the ValidationResult fullError

The **fullError** and in-fact any of the properties of the **ValidationResult** can be overriden.

```javascript
  // create a schema for that model
  const schema: EvaSchema = {
    surname : eva()
              .string()
              .required({fullError: "Nope that's not good!", 
                         shortError: "Dam!"}),
  };
```
```json
  "surname": {
    "id": "surname",
    "hasError": true,
    "fullError": "Nope that's not good!",
    "shortError": "Dam",
    "customeResult": {
      "fullError": "Nope that's not good!",
      "shortError": "Dam"
    }
  },
```


# String

The spec are a good source of documentation for how to use the string validators

https://gitlab.com/anthony-griff/govuk-eva/-/blob/master/src/string-validation/string-validation.spec.ts

## Required

Property must have a value.

```javascript
const schema = {
    name : eva()
            .string()
            .required()
      }
```

## RequiredWhen - Conditional Validation

Sometimes a field is only required if **(x)** is true, e.g If **date** is required but only when it's a Monday! That sort of thing. Or **date** is required but only when **name** is empty.
This validator takes a **whenPredicate**, it's a function that returns true or false. It's function is to: only run the required validator when the **whenPredicate** returns true.

Note : You might want to add the **validWhenEmpty** validator after the **requiredWhen**, this will stop the validation chain if the value is empty, after all sometimes it's required sometimes it's not.

```javascript
const schema = {
    name : eva()
            .string()
            .requiredWhen(() => true)
            .validWhenEmpty()
            .numbersOnly()
            .min(4)
            .length(4)
            .max(4,)
      }


const schema = {
    name : eva()
            .string()
            .requiredWhen(dateIsEmpty)
      }

const schema = {
    name : eva()
            .string()
            .requiredWhen(isMonday)
      }

```


## Pattern

String must match the regex.

```javascript
const schema = {
   age : eva()
        .string()
        .pattern(/^\d+$/)
}
```

## Min

String must be greater then or equal to the min length

```javascript
const schema = {
   title : eva()
           .string()
           .min(3)
}
```

## Max

String must be less than or equal to the max length

```javascript
const schema = {
   title : eva()
           .string()
           .max(3)
}
```

## Length

String must be the exact length

```javascript
const schema = {
   title : eva()
           .string()
           .max(3)
}
```

## NumbersOnly

String must be a number

```javascript
const schema = {
   title : eva()
           .string()
           .numbersOnly();
}
```

### Valid when empty

If you want an empty string to be valid the add the validWhenEmpty property to the validator. If the string is empty then the numbersOnly validator will not be triggered.

```javascript
const schema = {
   title : eva()
           .string()
           .validWhenEmpty()
           .numbersOnly();
}

```


## DateString

String must match one of the passed in date formats and be a valid date. Note, under the hood the validation system is using day.js to validate the date.

If you want to extend which date formats are valid see the day.js documentation.

"DD MMMM YYYY" : 02 March 2020
"DD MMMM YYYY" : 02 March 2020

```javascript
const schema = {
   title : eva()
           .string()
           .dateString(["DD/MM/YYYY", "DD-MM-YYYY"]);
}
```

This validator is just a wrapper around day.js, shown below :

```javascript
  return dayjs(dateString, acceptableFormats, 'en', strictMode).isValid();
```

## DateStringValidWhen

The model value is only a valid **dateString** when the predicate function returns true. Can be used when the **dateString** validation does not cover all the use cases.

```javascript
const schema = {
   title : eva()
           .string()
           .dateStringValidWhen(() => true);
}
```


## LessThan GreaterThan

This validator allow us to create an error when one string value is **lessThan** another string value. In this example **start** must be less than start. For the error message to read correctly you also need to pass in the name of the other property/field.

An alternative approach would be to just use the **errorWhen** validator, and write out the rules as a small function. In this can comparing one string to another.

```javascript
    const model = {
      start: 'Z',
      end: 'A'
    };

    const schema: EvaSchema = {
      start: eva()
            .string()
            .lessThan( { label: "End", value: () => model.to} )
    };

    //fullError : Start value must be less than End value
```


## ErrorWhen

The catch all validator, you must supply an error message you won't like the default!!

This validator allows you to generate an error on any predicate function. That is any function that returns true or false. The error is created when **true**.

In the example below, we want an error to be created for the **day** property, when today is a **Monday**. In the example **isMonday** is a function that returns **true or false**. Don't make the mistake of executing the **isMonday** function. You only need to pass the function NOT the result of executing the function. If you don't know the difference then ... well I can't help go google.

The error message can be any message you want, however you should include the name of the property/label in the error. i.e. "**Day** is invalid"

```javascript

const schema = {
   day: eva()
           .string()
           .errorWhen( isMonday, {fullError : "Day : I don't like Mondays", shortError : "Tell me why"})
}


const schema = {
   title : eva()
           .string()
           .errorWhen(isValidTitle, { fullError : "Title is too short", shortError : "You got a short error"})
}

```


## InArray

```javascript
  const model = { name: 'jone' };

  const schema = {
    name: eva().string()
               .required()
               .inArray(['bill', 'bob', 'barry', ''], { shortError: 'No Match' }),
  };

  const result = eva().validate(model, schema);
```





## Custom

If you want to do something more complex, then you can write your own validator!

In the example below, we have created a **mustBeBeforeLetter** validator, it should return a function of type:
(fieldRef: fieldRef): ValidationResult

In case that was not clear, a function that will return another function that takes a **fieldRef** and returns a ValidationResult!!

In your custom validator, **fieldRef.value** contains the field value. Validate that value is valid and return a ValidationResult with an 
appropriate error message.


```javascript

  const mustBeBeforeLetter = (beforeLetter: string) => (fieldRef: FieldRef): ValidationResult => {

    if (fieldRef.value > beforeLetter) {
      return {
        id: fieldRef.id,
        hasError: true,
        fullError: `Letter ${fieldRef.value} should be before ${beforeLetter}`,
        shortError: 'You got an issue!',
      };
    }
    
    return EmptyValidationResult(fieldRef.id);
  }

  const model = {
    nameStartLetter: 'D',
  };

  const schema = {
    nameStartLetter: eva()
          .string()
          .required()
          .custom(mustBeBeforeLetter("A"))
  };

  const result = eva().validate(model, schema);

```
### Compare field with field

Your custom validator, will also have access to the model if you want to do a comparison type validation, where one field is compaired to another.

If you really want you can ignore the **fieldRef.value** and just got straight to the **model**, you have access to the entire model and all the values.

```javascript

  const letterFromValidator = (fieldRef: FieldRef): ValidationResult => {
    if (fieldRef.value > this.model.nameEndLetter) {
      return {
        id: fieldRef.id, 
        hasError: true,
        fullError: `Letter ${fieldRef.value} should be before ${beforeLetter}`,
        shortError: 'You got an issue!',
      };
    }
    
    return EmptyValidationResult(fieldRef.id);
  }

  const model = {
    nameStartLetter: 'D',
    nameEndLetter: 'A',
  };

  const schema = {
    nameStartLetter: eva()
          .string()
          .required()
          .custom(letterFromValidator)
  };

  const result = eva().validate(model, schema);

```


## Valid when empty 

Stop the validation chain in it's tracks with the **validWhenEmpty** validator. There are two flavors to this validator shown below.

In the example below **appNumber** must be a number, but it's not a required field. So the **appNumber** isValidWhenEmpty(). We don't want to check that it's a number if it's empty.
We also don't want to check the lenght if it's empty.

So **validWhenEmpty** will stop the rest of the validator that appear after it from executing.

```javascript
    const schema = {
      appNumber : eva()
              .string()
              .validWhenEmpty()
              .numbersOnly()
              .min(10)
    };
```

### Config settings

This is a config setting on the string builder, it can be use to prevent the validation from running when the string is empty. As an example below age must be a number, but it's not required. Without the config setting, the numbersOnly validator would be triggered.

The setting stops all validators in the chain from running when set to true

```javascript
    const schema = {
      age: eva()
            .string({validWhenEmpty : true})
            .numbersOnly()
    };

```

Each validator also has a **validWhenEmpty** setting. In the example below the **numbersOnly** will not be triggered for empty values. NOTE : This at a different level to the **string** setting shown above.

```javascript
    const schema = {
      age: eva()
            .string()
            .numbersOnly({validWhenEmpty : true})
            .min(4, {validWhenWmpty: : true})
    };

```



# CheckOption

https://gitlab.nonprod.dwpcloud.uk/anthony.a.griffiths/govuk-eva/-/blob/docs-update/src/check-option-validation/check-option-validation.spec.ts

The checkOption validators are designed to validate an array, containing objects, where the objects have a property called checked!! If you want to validate a frontend CheckBox list then this is the way to go.

An example is probably the best way to understand this one. In the example below, one of the items in **myColour** must be have checked set to **true**, if it's not then the validation error is triggered.

```javascript
      const model = {
        myColour: [
          { text: 'Red', value: 'red', checked: false},
          { text: 'Blue', value: 'blue', checked: false },
          { text: 'Green', value: 'green', checked: false },
          { text: 'Yello', value: 'yellow', checked: false },
        ],
      };

      const schema = {
        myColour: eva({ label: 'What colours do you like?' })
                  .checkedOption()
                  .required(),
      };

      const modelState = eva().validate(model, schema);
```

## Required

One item in the array must be checked

```javascript
    const schema = {
        myColour: eva()
                    .checkedOption()
                    .required()
    };
```


## AtLeast(n)

To be valid at least 2 items must be checked, you can check more than 2 and that's just fine.

```javascript
    const schema = {
        myColour: eva()
                    .checkedOption()
                    .atLeast(2)
    };
```

## AtMost(n)

To be valid no more than two items should be true. If 3 items are checked then boom, validation error.

```javascript
    const schema = {
        myColour: eva()
                    .checkedOption()
                    .atMost(2)
    };
```





# SelectOption

https://gitlab.nonprod.dwpcloud.uk/anthony.a.griffiths/govuk-eva/-/blob/docs-update/src/select-option-validation/select-option-validation.spec.ts

The select option validator is designed, to work with the govuk-radio-inline and govuk-radio-group angular components.

There is only one validator and that is required.

```javascript
      const model = {
        nationality: { selected: '' },
      };

      const schema: EvaSchema = {
        nationality: eva()
                        .selectOption()
                        .required()
      };

      const modelState = eva().validate(model, schema);
```

---
_Source: https://npm.io/package/govuk-eva-validation · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
