Skip to main content
Unlisted page
This page is unlisted. Search engines will not index it, and only users having a direct link can access it.

Custom fields

How to use the Custom fields in an Iris App

When you have a Tile Extension and a Tile App, you would probably like to store some data from your tile.
To do this, you need to define one or more custom fields in the tile manifest. When a field has been defined, a value can be set per asset using the tile runtime.
The definition is created once the tile is approved and shared accross all assets. Whereas the value can be set on each asset by the tile developer.

1. Defining a custom field

To define a custom field, you need to add a customFieldDefinitions array to your tile manifest:

customFieldDefinitions: [
{
type: 'STRING',
entityType: 'ASSET',
key: 'myKey',
title: 'String Field',
uiEditable: true,
uiVisible: true,
defaultValue: 'Some default value',
}
]
  1. type defines the custom field type (see below for all options).

  2. entityType defines what type of entity a custom field can be used on. Currently only ASSET is supported.

  3. key defines the programmatic name of the field and cannot be changed after it has been created.

  4. title defines the UI visible name of the field.

  5. uiEditable / uiVisible controls how the field will be shown in the manager UI. This does not limit how the field is used inside the tile.

  6. defaultValue TBD (might not make sense)

Apart from the these standard properties, some field types have extra properties defined below.

All custom fields defined by a tile will be owned by the tile, and the tile developer should consider existing data when changing field definitions.

Custom field types

Field typeDescriptionExtra properties
BOOLEANBoolean value.
DATEDate stored in ISO8601 format.
DROPDOWNA predefined list of options. Supports both single and multi selects.allValues, multiSelect, valueReplacements
EMAILEmail field. Will be rendered with a mailto link in the UI.
NUMBERNumericminimum, maximum, isInteger
PHONE_NUMBERPhone number stored in E.164 format. Validated using Google libphonenumber.
STRINGFree text fieldminimumLength, maximumLength, pattern
STRING_LISTList of free text valuesmaximumItems, itemMinimumLength, itemMaximumLength, pattern
WEB_ADDRESSWeb address. Will be shown as a link to open a new window in the UI.
JSONJSON values.
MONETARYMonetary values with currency in ISO 4217 standard.minimum, maximum
FILEFile reference. Files are uploaded via a presigned URL. Maximum file size is 20 MB.

Type specific properties

PropertyField typeRequiredDescription
maximumNUMBERNoMaximum numeric value
minimumNUMBERNoMinimum numeric value
isIntegerNUMBERNoDisallow decimal values
maximumLengthSTRINGNoMaximum length of the text
minimumLengthSTRINGNoMinimum length of the text
patternSTRINGNoThe allowed regular expression. Syntax is documented here.
itemMinimumLengthSTRING_LISTNoMinimum length of each string item
itemMaximumLengthSTRING_LISTNoMaximum length of each string item
maximumItemsSTRING_LISTNoMaximum items allowed in the list
allValuesDROPDOWNYesAll allowed values
multiSelectDROPDOWNNoAllow multiple options to be selected. Default false
valueReplacementsDROPDOWNNoMap from old values no longer allowed to new values. Used for updating existing data
currencyMONETARYYesCurrency in ISO 4217 standard

2. Using a custom field

Programmatic access

First install the Iris App Runtime if you haven't got it already.

npm i @trackunit/iris-app-runtime-core

Using the CustomFieldRuntime you will be able to obtain all custom fields values and definitions owned by the tile:

import { CustomFieldRuntime, CustomFieldType } from '@trackunit/iris-app-runtime-core';

const customFieldRuntime = new CustomFieldRuntime();

// Get all custom field values and definitions for your asset ID.
const myCustomFieldsPromise = customFieldRuntime.getCustomFieldsFor({
type: 'ASSET',
id: '<my-asset-id>',
});

The API returns both the definition of the custom field and any value saved on the given asset ID.
If a value has not been saved for the asset ID only the definition will be returned.

To save a custom field value you need to provide the definition key, the entity Id and the new value:

// Save a custom field value
customFieldRuntime.setCustomFieldsFor(
{
type: 'ASSET',
id: '<my-asset-id>',
},
[
{
definitionKey: 'myKey',
value: {
type: CustomFieldType.STRING,
stringValue: 'My new value',
}
}
]
);

UI components

To ease the life of the tile developer we provide a React UI component that renders a custom field input box according
to a custom field definition.

First install the Tile Runtime if you haven't got it already.

npm i @trackunit/custom-field-components
import { CustomField } from '@trackunit/custom-field-components';

With the component it is possible to render an input component for any custom field by providing a field retrieved from getCustomFieldsFor to the component:

<CustomField
field={field}
key={field.definition.key}
register={register}
formState={formState}
setValue={setValue}
/>

This complete example demonstrates how to render all custom fields owner by the current Iris App:

import React, { useEffect, useState } from 'react';
import {
CustomFieldRuntime,
AssetRuntime,
AssetInfo,
ValueAndDefinition,
} from '@trackunit/iris-app-runtime-core';
import {
Button,
Card,
CardBody,
CardFooter,
CardHeader,
} from '@trackunit/react-components';
import { CustomField } from '@trackunit/custom-field-components';
import { useForm } from 'react-hook-form';
import { TrackunitProviders } from '@trackunit/react-core-contexts';

const assetRuntime = new AssetRuntime();
const customFieldRuntime = new CustomFieldRuntime();

export const App: React.FC = () => {
const [customFields, setCustomFields] = useState<ValueAndDefinition[]>();
const [asset, setAsset] = useState<AssetInfo>();
const { register, handleSubmit, formState, setValue } = useForm({
shouldUnregister: false,
});

useEffect(() => {
(async () => {
const updatedAssetInfo = await assetRuntime.getAssetInfo();

setAsset(updatedAssetInfo);
const myCustomFields = await customFieldRuntime.getCustomFieldsFor({
id: updatedAssetInfo.assetId,
type: 'ASSET',
});
setCustomFields(myCustomFields);
})();
}, []);

return (
<TrackunitProviders>
<Card>
<CardHeader
heading="Custom Fields"
subHeading="Showcase for custom fields."
/>
<CardBody>
{customFields?.map((field) => {
return (
<CustomField
field={field}
key={field.definition.key}
register={register}
formState={formState}
setValue={setValue}
/>
);
})}
</CardBody>
<CardFooter>
<Button
onClick={handleSubmit((data) => {
customFieldRuntime.setCustomFieldsFromFormData(
{
id: asset?.assetId || '',
type: 'ASSET',
},
data,
customFields || []
);
})}
>
Save Changes
</Button>
</CardFooter>
</Card>
</TrackunitProviders>
);
};

3. Local development

When running in local dev mode the tile runtime will store all custom fields in local storage in the browser.

This means that tile developers can easily change custom fields and add new fields without getting a new tile version approved.

Please note, that since the custom fields only exists in local storage inside the browser any values stored during local dev mode will only be available inside the tile and not in other parts of the UI.
The validation rules will also be less strict since we are only emulating a custom fields backend.

4. Deploying a new custom field

When a tile package with a custom field is published, we will validate it during the publish flow. So watch out for any warnings during publish. After publish, we will review the tile and when it is approved we will save the new field definitions.

If any existing values do not follow the new definition, they will be replaced with the default value.