Scripts in widgets

Widgets are one of the basic elements that allow you to build interfaces. Widgets can be created and accessed inside a workspace or an app. You can also add global widgets for the whole company. Read more about widgets in the Set up interfaces article.

You can use code in scripts to implement complex logic for system objects, manage data, or change the layout of a widget. Script code can include functions, custom classes, and interfaces written in TypeScript.

Depending on where they are executed, scripts in widgets can be divided into two groups:

  • Client-side scripts are executed in a client’s browser. They are limited by the access permissions of the user that they run under.

  • Server-side scripts are executed on the server with the administrator’s access permissions, but they execute a limited number of functions per minute.

Access to data. Widget roles

In scripts inside a widget, you can access global constants and data that can be different depending on the widget’s role. Specifically, you can access the Context global constant.

Global widgets

Global widgets are widgets created in Administration / Interfaces and available in all the company’s workspaces. A widget like this only has properties from its own context (added on the Context tab in the interface designer when editing the widget) and system properties with codes starting with underscores. In scripts, these properties can be accessed via the Context constant.

    Context.data.string_1 = 'string value';

Widgets associated with workspaces and apps

Widgets can be created in a workspace’s interfaces. In this case, they will be associated with the workspace they were created in and available to other widgets in this workspace. Widgets can also be created in the interfaces of a separate app. Then they will be available to other widgets in this app and to widgets of the workspace the app is located in. However, no matter the level, a widget only has access to the properties of its own context.

You can reuse widgets that you create: it’s possible to add one widget into another or several other widgets without any limitations on nesting levels. This allows you to build complex interfaces. Note that even in this case, in a widget you can only access properties from its own context. Properties of nested widgets will not be available.

Form widgets

You can create custom forms for apps and business processes. For each custom form, a separate widget is automatically created and assigned to this form. Form widgets also have their main context (Context), but it includes the properties of the object that the form is configured for. For example, if a widget is an app’s form, it is possible to access the app’s properties in its context. The same is true for forms in business processes: in these widgets, you can work with properties from the process context.

Apart from the main context, widgets have View context that you can access in scripts using the ViewContext constant. View context includes properties directly added to the form. This means that widgets of the same app or process have the same properties in the main context, but their View context can be different.

    const itemName = Context.data.__name;
    ViewContext.data.string_1 = `Name ${ itemName }}`;

Functions

Scripts in widgets are basically a set of functions. When a widget loads, scripts are loaded and transformed into JavaScript, and the context is formed. Actual execution of certain functions happens individually when they are called.

System functions

Widgets have system functions that run automatically at certain key points defined by the system. If you want a function written in a widget’s scripts run at one of these points, assign it to the necessary system function. You can do it in the Interface designer on the Settings tab in the System functions section.

  • Initialization. This function runs when the widget is initialized. It can be used to initialize the starting values of context properties, get necessary data, define the layout of the widget when it loads, or make other functions run upon the widget’s initialization.

Example:

async function onInit (): Promise<void> {
    Context.data.counter = 0;
    setStartValues();
}
  • Display needed. This function is executed right before the widget is rendered. It defines whether it needs to be displayed. This function has to return a Boolean type value. If it returns false, the rendering of the widget is cancelled.

Example:

async function canRender (): Promise<void> {
    return !!Context.data.app;
}
  • Validation. This is a function for custom validation. It is executed by a creation or editing form of an app when an item is saved. It runs along with the standard form validation. The function has to return the validation result object of the ValidationResult type. If this object doesn’t contain errors, custom validation will be considered successful, and the item will continue to be saved or edited. If the object contains at least one error message, the saving or editing process will be interrupted. Validation is applied to the main form and all nested widgets if they have a validation function. The validation results for all widgets are added up and shown on the main form.

Example:

async function validation (): Promise<ValidationResult> {
    const result = new ValidationResult();
    if (!Context.data.string1 || Context.data.string1.length < 10) {
        result.addContextError('string1', 'The string needs to be at least 10 characters long');
    }
    return result;
}

Custom functions

All functions written in widget scripts are custom functions. You can call a function like this from another function by specifying its name and arguments that need to be passed in the parentheses.

async function onInit (): Promise<void> {
    const result = getValues();
    ...
}

function getValues (): Promise<any> {
    return {
        property_1: Context.data.prop1,
        property_2: `some string`,
    };
}

Call a server function from a custom script

To make a server function run when a custom function is executed instead of calling it directly, use the Server constant.

Server function:

async function DoSomeWork() : Promise<void> {
    // This sets the common logic of the server script
    // You can also make external calls using `await`
    let response = await fetch('https://my-service.mycompany.com/getmydata?token=' + Context.data.secureToken);
    Context.data.mySecureData = await response.text();
}

Custom function:

async function onButtonClick() : Promise<void> {
    ViewContext.data.blockUI = true;
    await Server.rpc.DoSomeWork();
    ViewContext.data.blockUI = false;
}

Call a function from nested widgets

Besides all ways to call a function listed above, you can also do it from a nested widget. For example, you can add the Button widget and select Script as the action type in its settings. Then you can select the function you need in the On click handler field.

Here you can choose the type of function (Client or Server) and select a function from the drop-down list.

Different widgets have different properties that allow you to call a function. For example, in the Tabs widget, you can configure an On tab change handler. The settings are generally similar to the ones described earlier: you need to select the function’s type and name to associate it with an event. These settings can usually be found on the Events tab in a widget’s settings.

Moreover, all widgets have system events, On mouse enter handler and On mouse leave handler. They can usually be found on the System tab in a widget’s settings.

Call a function from the Code widget

To call a client script from the Code widget, use the following construction: <%= Scripts %>.FUNCTION_NAME():

    <button onclick='<%= Scripts%>.OpenPopup()'>Open popup</button>

When you call a function from scripts written inside attributes (for example, onclick), you can only pass a string to it, not the whole object. For example:

    <button onclick='<%= Scripts%>.check('<%= someObject.__id %>')'>Execute</button>

Call a function from a widget file

You can upload files to widgets. If you add a file with JS code to a widget, you can call a function from the file. To do that, you need to connect the JS file from the widget’s files in the Code widget. For example:

    <script type='text/javascript' src='<%= UI.widget.filePath %>/test2.js'></script>
    <button type='button' onclick='onClickAction()'>Button</button>