Transformers and Validators
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]
Data Provider | Description |
---|---|
countries |
Country codes based on the ISO 3166 alpha-2 standard. |
currencies |
Currency codes based on the ISO 4217 standard. |
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]
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]
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.
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}
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}
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.common14 };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.common26 }));27 }28 }29 }30}