Transformers and Validators

Data Providers

If you are looking to fetch data asynchronously from your API or provide suggestions as your users type in the spreadsheet editor, then you are in the right place.

Let's see how we can extend our Contacts example, to provide suggestions based for the country field on the ISO 3166 alpha-2 standard. For this purpose we will be using the countries provider. Please note that in contrary to transformers and validators, you can define only one provider.

1[
2 {
3 "label": "First name",
4 "transformers": "trim|capitalize"
5 },
6 {
7 "label": "Last name",
8 "transformers": "trim|capitalize",
9 "validators": "required"
10 },
11 {
12 "label": "Phone",
13 "transformers": "trim"
14 },
15 {
16 "label": "Email",
17 "transformers": "trim|lowercase",
18 "validators": "required|email"
19 },
20 {
21 "label": "Country",
22 "transformers": "trim|uppercase",
23 "provider": "countries"
24 }
25]

Available data providers

Data Provider Description
countries Country codes based on the ISO 3166 alpha-2 standard.
currencies Currency codes based on the ISO 4217 standard.

Transformation

In many cases, you may have a file to be imported that contains raw values, and you want to display the corresponding labels in the editor (e.g., displaying the country name instead of the country code). Similarly, you might have labels that you wish to convert back into their corresponding codes. To achieve this, you can use a transformer, as shown below:

1[
2 {
3 "label": "First name",
4 "transformers": "trim|capitalize"
5 },
6 {
7 "label": "Last name",
8 "transformers": "trim|capitalize",
9 "validators": "required"
10 },
11 {
12 "label": "Phone",
13 "transformers": "trim"
14 },
15 {
16 "label": "Email",
17 "transformers": "trim|lowercase",
18 "validators": "required|email"
19 },
20 {
21 "label": "Country",
22 "transformers": "trim|as:countries",
23 "provider": "countries"
24 }
25]

Validation

Data providers are being used to provide relevant suggestions as your users type in the Spreadsheet editor and try to fix the uploaded data. By design, data providers will not apply any form of data validation. This allows us to use data providers only for suggestions without enforcing a strict list of allowed values. For example, when providing tags for a blog post.

You can use the in validator to indicate that only values returned by the data provider are acceptable. Let's update our example to ensure that only country codes provided by the ISO 3166 alpha-2 standard are allowed.

1[
2 {
3 "label": "First name",
4 "transformers": "trim|capitalize"
5 },
6 {
7 "label": "Last name",
8 "transformers": "trim|capitalize",
9 "validators": "required"
10 },
11 {
12 "label": "Phone",
13 "transformers": "trim"
14 },
15 {
16 "label": "Email",
17 "transformers": "trim|lowercase",
18 "validators": "required|email"
19 },
20 {
21 "label": "Country",
22 "transformers": "trim|as:countries",
23 "validators": "in:countries",
24 "provider": "countries"
25 }
26]

Custom data providers

In addition to the built-in data providers, you can create custom providers to offer dynamic suggestions and perform advanced data validation as users type. These providers can utilize arrays or key-value objects, or even connect to your APIs to retrieve data in real-time.

From arrays

Let's start with a simple example on how you can add a custom data provider, if you already have an array of values. We are adding a custom provider that provides the list of planets. To achieve we will be using the GenericProvider class.

1{
2 fields: {
3 "planet": {
4 validators: "required|in:planets",
5 provider: "planets"
6 }
7 },
8 providers: {
9 planets: GenericProvider.fromArray([
10 'Mercury',
11 'Venus',
12 'Earth',
13 'Mars',
14 'Jupiter',
15 'Saturn',
16 'Uranus',
17 'Neptune',
18 'Sun',
19 ])
20 }
21}

From objects

Similarly to above, you can also add a custom data provider, if you already have a key value object. In the following example, we are adding a custom provider that provides the list of US States. To achieve we will be using the GenericProvider class.

1{
2 fields: {
3 "state": {
4 validators: "required|in:states",
5 provider: "states"
6 }
7 },
8 providers: {
9 states: new GenericProvider({
10 AL: 'Alabama',
11 AK: 'Alaska',
12 AS: 'American Samoa',
13 AZ: 'Arizona',
14 AR: 'Arkansas',
15 CA: 'California',
16 CO: 'Colorado',
17 ...
18 });
19 }
20}

Advanced custom providers

In the example below, we register a custom data provider named restcountries, which connects to https://restcountries.com to asynchronously fetch a list of countries. Unlike the previous examples, this implementation is more advanced and powerful, as it involves connecting to an API to retrieve the data.

get

Description Callback to fetch one item by value. Useful for validations.
Type get(value: string): { value: string, label: string }
Default None

find

Description Callback to fetch multiple results based on the provided query. Useful in typeahead suggestions
Type find(search: string): { value: string, label: string }[]
Default None
1{
2 providers: {
3 restcountries: {
4 get: async function (query) {
5 const response = await fetch('https://restcountries.com/v3.1/alpha/' + query + '?fields=name,cca2');
6 if (!response.ok) {
7 throw new Error(`Response status ${response.status}`);
8 }
9 
10 const data = await response.json();
11 return {
12 value: data.cca2,
13 label: data.name.common
14 };
15 },
16 find: async function (query) {
17 const response = await fetch('https://restcountries.com/v3.1/name/' + query + '?fields=name,cca2')
18 if (!response.ok) {
19 throw new Error(`Response status ${response.status}`);
20 }
21 
22 const data = await response.json();
23 return data.map(item => ({
24 value: item.cca2,
25 label: item.name.common
26 }));
27 }
28 }
29 }
30}