Section author: Jonathon Love

3. Advanced Customisation

3.1. Customizing UI controls

Sometimes the .u.yaml file alone doesn’t provide enough flexibility for an analysis options UI. More complex UIs can be achieved through writing additional javascript that provides more complex behaviour. This allows much greater freedom and flexibility, and allows you to design an options UI completely from scratch if necessary.

The options UI is customised through attaching javascript ‘event handler’ functions to the user interface. These then respond to events such as a UI ‘loaded’ event, or an option ‘changed’ event in response to a user interaction.

Additional UI functions are defined in a javascript file with a .js suffix, and are placed in the jamovi/js folder of the module. The name of the .js file should match the names of the other .yaml files for that analysis. For example:

  • ttest.a.yaml

  • ttest.r.yaml

  • ttest.u.yaml

  • js/ttest.js

The events .js files follow the CommonJS javascript module spec, assigning each of the event handlers and functions to an object which is assigned to module.exports.

IMPORTANT: For customisations to function, the jus property in the u.yaml file needs to be set to 3.0.

For example:

name: ttest
title: T-Test
jus: '3.0'
compilerMode: tame
children:
  - type: ComboBox
    name: ttestType
module.exports = {

    // event handlers and functions are defined here

    // this is an example of an event handler
    view_loaded: function(ui, event) {
        // do something
    },

    // this is another example of an event handler
    ttestType_changed: function(ui, event) {
        let value = this.calculateValue();
        // do something
    },

    // this is an example of an auxiliary function
    calculateValue: function() {
        // do something
    }
};

In the above example, the view_loaded(...) event handler is invoked with a loaded event when the analysis options UI is created for the first time. Similarly, a changed event is fired, and the event handler function ttestType_changed(...) is invoked when the user changes the value of the ttestType option.

Note that jamovi ships with the Chrome developer tools, so it’s possible to invoke these, and have access to the debugger, DOM viewer, etc. The dev tools are invoked by pressing F10. (Note that sometimes jamovi’s internal iframes prevent this key stroke from registering. You sometimes need to click the blue bar along the top to move the focus back to the main window, before the F10 keystroke will register.)

3.1.1. Events

Event handlers are added by naming the function with the following pattern. Option name, followed by an underscore, followed by the event name. {optionName}_{eventName}

For example: ttestType_changed

jamovi will automatically attach the event handler ttestType_changed(...) to the changed event of the ttestType option when the analysis is run.

3.1.1.1. View

Event

Description

creating

Invoked at the very beginning of the options panel creation, before anything is added to the DOM.

loaded

Invoked after the options panel has been created and the DOM has been populated. The same analysis options panel persists as long as jamovi is still running, and is shared by all analyses of the same type, so this is only invoked once. This is the most common event to use for customising the UI.

updated

Invoked when the user selects a different analysis (of the same type) and the options need to change to reflect the new analysis’ option values (This is also called when the user inserts a new analysis, when an option panel for that analysis type already exists).

3.1.1.2. All controls

Event

Description

changing

Invoked before the value of the control is changed.

changed

Invoked after the value of the control is changed.

3.1.1.3. ListBox

Event

Description

listItemAdded

Invoked when a control is added to a list box.

listItemRemoved

Invoked when a control is removed from a list box.

3.1.1.4. Suppliers

Event

Description

updated

Invoked when a variable name or level name is changes.

IMPORTANT NOTE: The use of the updated event is required for all Supplier and VariableSupplier controls (under certain conditions). If it is not implemented then jamovi will display the following error:

Error: The use of a ‘VariableSupplier’ control, with the property >
populate: ‘manual’, requires an ‘updated’ event handler to be
assigned.

or:

Error: The use of a ‘Supplier’ control requires an ‘updated’ event
handler to be assigned.

These suppliers require manual population and therefore need to be appropriately updated in response to variable or level name changes.

3.1.1.5. Custom Control

Event

Description

cr eating

Invoked during the creation phase of the options panel. Allows for access to the custom control for customisation before it is made visible.

u pdated

Invoked when a variable name or level name is changes.

3.1.2. Event handlers

All event handlers are invoked with two arguments; the ui, and the event. The ui is a convenience object containing all the other controls in the options panel and the DOM. All the option controls are available in the ui argument as properties. For example:

ttestType_changed(ui, event) {
    let ttype = ui.ttestType.value()
    if (ttype === 'welchs')
        ui.priorWidth.setValue(0.707)
}

In this example, when the user changes the ttestType option to welchs, the priorWidth option is changed to 0.707. All options have the .value() and .setValue() methods.

Note that each change to the values of the options triggers the re-running of the analysis. If multiple option values need to change in response to an event, it is better to batch these changes together, to prevent the analysis being restarted again and again. Option changes can be batched together with the ui.view.model.options beginEdit() and endEdit() functions as follows:

ui.view.model.options.beginEdit();
ui.figWidth.setValue(400);
ui.figHeight.setValue(300);
ui.view.model.options.endEdit();

In this example, changing the figWidth and figHeight options only triggers the re-running of the analysis once.

3.1.3. Accessing the DOM

The DOM for the root of options UI can be accessed from the ui through the view property:

ui.view.el  | the root DOM node
ui.view.$el | the root DOM node as a jQuery object

It is also possible to inspect the DOM using the chrome dev tools shipped with jamovi. (To access these, click the blue bar at the top of jamovi, and press F10.)

Additionally, the DOM elements for most of the option controls are accessible through the el and $el property. i.e.

let figWidth = ui.figWidth.el
let $figWidth = ui.figWidth.$el

3.2. Adding a custom control

Sometimes the controls built into jamovi do not provide the behaviour your analysis requires. In this scenario, it’s possible to create a ‘custom control’ which is placed within the DOM.

A control of type CustomControl allows for this possibility, when added to the .u.yaml file, and the creating event is handled in the javascript.

For example,

The description of the control in the u.yaml

- type: CustomControl
  name: ttestType

and the event handler for the the .js

ttestType_creating: function(ui, event) {
    let $element = ui.ttestType.$el;
    // in this instance, the $element object represents the root DOM node
    // of the custom control. sub-nodes can be added to this node, and the
    // control will be laid out by the layout manager in the final options
    // UI
}

3.3. Options UI from scratch

Sometimes an analysis requires a very radical UI design that can’t be accommodated by the standard UI controls or a custom control. If this is the case, an entirely custom UI may need to be developed.

To achieve this, the options and their types are defined as usual in the .a.yaml file, however, each option is marked as hidden: true. This prevents jmvtools from (re)adding the standard UI controls into the .u.yaml file, allowing you to implement them yourself.

To construct the UI, all the DOM setup for the custom panel should occur in a creating event handler for the view control.

'use strict';

module.exports = {

    view_creating: function(ui, event) {
        let $panel = ui.view.$el;
        // in this instance, the $panel object represents the root DOM node
        // of the options panel. sub-nodes can be added to this node.
    }
}