Custom fields allow you to create components that implement specific logic or UI using JavaScript code.
With this feature you can easily pass data around, make client and server-side validations and handle common events like focus or blur. Some use cases for using custom fields could be:
- Fields with a custom data structure (e.g. objects, array of strings...).
- Fields that use 3rd party widgets (e.g. Google Address autocomplete).
- Fields with logic to hide/show other fields.
- Fields which require external APIs to get a value.
Getting started
Let's start imagining that we want to implement a text input that allows the user to type an IBAN account number, with the following features:
- Length validation: 16-34 characters.
- Country validation: the IBAN must begin with the country code that the user selected in a previous step.
- Input placeholder: it will start with the selected country code so the user is hinted on the correct format.
- Removal of spaces.
- Tokenization of the account number through an external API.
We will be checking only the length and the country of the account number. In the real world you should probably check the validity of the number itself with the available algorithms, but that's a bit out of scope for this introduction.
Let's start with the minimal implementation: in the "source code" field we have to type a function that returns an object with some specific methods, like so:
These methods are "hooks" that get called at different times in the lifecycle of the component that your custom field implements. They get called one or more times, in any order, depending on the structure of your form and the way the user interacts with it.
We won't go through all of the available methods now, but in this example you can see we implemented these 4:
- init(): called once, when the field is created.
- block(): called when the field must be blocked from all input. This happens when the SDK is gathering all the data to send it to Arengu, for example. Since the SDK doesn't know how your field does this, it relies in your implementation to do so.
- unblock(): same as block(), but for unblocking.
- getValue(): called when the SDK needs the value of your field. This can be used to send it to Arengu, to emit DOM events or to make validations.
Let's now implement the placeholder to hint the user on the expected IBAN format.
Parameters
Inside a custom field code you don't have access to all the {{references}} like in the rest of Arengu. In order to make some external value available inside, you have to use the "Params" section inside the custom field settings.
In this case, just before our custom field there was be a previous step where the user selected their country in a dropdown field, and its identifier was countryCode. Thus we can make available that reference inside the custom field, adding the param countryCode as a reference to {{fields.countryCode}}:

We then would access the parameter using {{params.countryCode}} like so:
This code works but it has a problem: since init() is called only once, if params.countryCode changes in a subsequent render of the field (for example, because the user went back and selected a different country), we will not be showing the updated placeholder text!
Luckily we can make it work properly implementing the update() method, that will be called as soon as there's a change in the parameters:
Validation
We can now add some validation to the field.
Mind that this should be some light, client-side validation, in order to improve the experience for the user: if you want to be sure that your form receives the correct data shape, you must use the JSON schema feature, which validates the data server-side, and thus cannot be tampered with.
But for our case, it's useful to quickly check client-side the length or the country of the IBAN, for example.
Let's change the getValue() method a bit:
Note that any Error that is thrown inside the getValue() method will be shown as a validation error below your custom field, just in like a normal Arengu form field.
Asynchronous values
Let's suppose that for security reasons we don't want to receive the real IBAN number, but we want to delegate and send it to an external API that tokenizes it for us. That way Arengu only receives the tokenized value instead.
This call to an external API presents us with a problem: it's asynchronous, so we don't know how much will it take, and in the meantime we don't want the user to think something is broken because of the lack of visual feedback.
But it can be fixed easily: if your implementation of the getValue() returns a Promise, the Arengu SDK will wait for it to resolve, showing the user a spinner in any submit buttons in order to indicate progress.
Make these changes in your code:
Final result
For your convenience, here is the complete source code for the custom field that we just implemented: