Table of Contents

How To Create Custom Input Fields (v11.2+)

you are here (click to zoom) - discover the stack

Sometimes you want a custom input field - as color-picker, dropdown-from-api or whatever.

Tip
  1. 2sxc 11 finally allows you to do this using simple WebComponents
  2. Registering these happens by placing them in a specific folder
  3. You can also make them configurable by placing a content-type json in another folder
Note

There are more ways to provide and register custom input fields - like when you need them globally across many apps and portals.

Quick-Start Video and Tutorial

If you're new here, best watch this video:

Then check out the tutorials.

What kind of Custom Input Field can you Create

You can create any kind of custom input field, as a JavaScript WebComponent.

  1. Look and Feel however you want it
  2. Any kind of JS code
  3. Talking to any other system (Google Maps, etc.)
  4. Talking to any endpoint (weather APIs)
Tip

This overview will get you started, but we've already created demos on the 2sxc Tutorials. If you want to know more, you should also read the specs

Getting Started with Custom Input Fields

Basically a custom Input Field is just a index.js in the correct folder. These are the specs:

  1. An input field as described here is an App Extension.
    1. All App Extensions must each lie in an own folder...
    2. within a folder called system inside the App folder
  2. The folder name for your custom input field must obey certain naming rules so that they are auto-detected.
  3. The javascript that will be loaded must be called index.js
  4. Your script must register a custom element - a WebComponent - in the browser
  5. The name of your custom element is predefined, and must adhere to the naming rules.

Here's a checklist to get this setup

Note

Once you have that setup, the input field is automatically detected and a user can choose it as a field type in the configuration.

Some Background on WebComponents

  1. The WebComponent has a simple lifecycle - from when it's created to when it receives data and can push changes back to the form.
  2. The form itself is reactive. This means that your field will receive messages when the value changes or when other values change (in case you want to use other field values in your input).
  3. The API to communicate with the form has a few complexities you need to know. This is because the form is very dynamic - so the user could switch languages, and your input field needs to react to this.

So let's get started 🚀!

Getting the HTML into the Custom Input

WebControls are developed using pure JavaScript, but a control is automatically a rich DOM object. So your this object can do all kinds of DOM manipulations, but in most cases you'll just do something like this:

this.innerHTML = 'Hello <em>world</em>!';

Now you have to wait with doing this, till your object has been added to the DOM, so you need to kick this off in the connectedCallback() like this:

class EmptyHelloWorld extends HTMLElement {

  /* Constructor for WebComponents - the first line must always be super() */
  constructor() {
    super();
  }

  /* connectedCallback() is the standard callback when the component has been attached */
  connectedCallback() {
    this.innerHTML = 'Hello <em>world</em>!';
  }
}

Read and Write Values

The 2sxc form will initialize your custom element and attach a connector object. This happens automatically, so you will have it once connectedCallback() is fired. This connector is a rich object with lots of stuff, but for your field value you need to know these bits

  • connector.data.value gets you the current value
  • connector.data.update(newValue) updates the form with the changed value
  • connector.data.value$ is the observable version of the value - this is great for advanced use cases, but otherwise you can stick to the simple .value
Tip

Avoid calling update(...) if nothing changed - as it will make the form dirty, so the user will be asked if he wants to save when cancelling the dialog, even though nothing changed.

Tip

Check out this tutorial example of Pickr to see all this in action

Load Custom CSS and JS Libraries

Since this is all standard JavaScript, you can do it anyhow you want. For example, to load some CSS we recommend that you simply add a <link> tag to your html, like this:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/themes/classic.min.css"/>

For JavaScript you can do the same, either using a <script> tag or telling the browser to load the JS using DOM commands. We also provide a helper on connector.loadScript(name, url, callback) which does the following:

  1. Check if the name given in the first parameter exists on the window object (to check if it's already loaded)
  2. If not, load the script provided in the url
  3. Watch the window object using polling to see when the item with name is created
  4. Then trigger your callback function
Tip

Check out the tutorial example of Pickr to see all this in action

Make your Fields Configurable

Now you have a color-picker, but each field may require a different set of preconfigured colors. Or maybe your date picker has could optionally restrict dates to weekdays. In these cases, you need configuration specific for the field.

Tip

2sxc 17 introduces the ability to configure exactly what content-types are provided to configure the Input-Type.

So without this config, the default system always uses a @All, @Xyz where Xyz is the name of the data eg. String, and @exact-name-of-the-input-type eg. @string-my-picker.

With the config, you can eg. skip the @String even if you have a string field.

Create Your own WYSIWYG Field

WYSIWYG fields are very hard to do right. Basically you can simply create your own using the same principles as mentioned above. But we recommend that you use the existing WYSIWYG field provided by 2sxc and just change some of the configurations.

Tip

By just reconfiguring the existing 2sxc WYSIWYG you will benefit from ADAM file-upload and continuous updates to the main component.

Here's what you need to know

  • The WYSIWYG field is based on TinyMCE - so to make configuration changes, you'll need to understand that API pretty well.
  • To change it, you need to create a wrapper component which contains the standard 2sxc-wysiwyg and give it different configurations.
  • To do this, we are calling various methods on a reconfigure object of your wrapper - so you can override most of the defaults

To learn more, best look at the tutorials and the API

Register Global Custom Fields

Global custom fields consist of a few more steps, because they need to be registered in a way that all apps and portals can see them. This is done by placing them in a special folder and registering them in a special way.

Note that these docs were added in v20 because iJungleboy noticed that it needs some docs, but this is still very drafty.

Create the Global Configuration

If your custom field does not have any configuration

  1. create a ContentType-InputType entity with all the specs (in the Fields scope).

If your custom field has configuration

  1. First create the configuration content-type as normal in your app - recommended in the Fields scope.
  2. Assuming that you'll also need to load a JS, you must now decorate this configuration type with Metadata of the type ContentType-InputType in the Fields scope and give it all the configuration you need.

Distribute the Global Configuration

If you only want the configuration to work on the system you created it in, you're done. Otherwise export the configuration (either the entity, content-type, or bundle)... ...and place it in the global App_Data\system-custom\ folder(bundles or entities, depending on your export).

Internal Notes

This is just internal, so we don't forget how this works. For the fields to do what they should, it needs these things to work:

1. Field Registration

The backend must know about the field - for example, to provide it in the list of possible input-types when creating/configuring a field. This happens differently depending on whether it's a global field or an app-specific field.

In a global field, it must be registered in as a ContentType-InputType entity in the Fields scope. This is either done directly in the system, or by loading it from a preset JSON file in the App_Data\system-custom folder.

In an app-specific field, just having the folder with the JS file /system/field-[type]-name/index.js is enough for auto-detection. But you can also have a content-type describing it more, either by creating an isolated ContentType-InputType entity in the Fields scope, or by creating a configuration content type like @[type]-name like @string-my-picker. This can then again be decorated with Metadata of the type ContentType-InputType in the Fields scope - for example if you want to give it a nice name in the UI, and/or to provide more configuration such as other assets to load.

2. Field JS Detection

The Edit-Form must be able to detect the field JS files and load them into the browser. The backend provides the JS to the front-end. This again is different in global and local fields.

  • in global fields, the information must be in the entity of type ContentType-InputType in the Fields scope.
  • in app-specific fields, the detection happens automatically, but can be overruled by providing metadata of the type ContentType-InputType in the Fields scope.

3. Field JS Loading

The front-end will load the JS files provided by the backend. This happens automatically, assuming the information was found in step 2.

4. Field Rendering

The front-end should have a custom element registered matching the field type. This happens automatically when the JS file is loaded, assuming the code is correct. The name of the custom element must be field-[type]-name where [type]-name is the name of the field type.

If anything doesn't work, you will not see your custom field.