Component Events

Component events (also known as DOM events) are similar to page events, except that they are fired by components on a page (or other container).

A component event listener can have any name, and is generally associated to a component event property via the binding expression on the component markup. Component event listeners are defined in the Page (or container) module under the eventListeners property, much like other Visual Builder events. For example, an event listener for the selectionChange event for the <oj-tab-bar> component can be defined within the eventListeners section as:

"eventListeners": {
  "onSelectionChange": {
    "chains": [
      {
        "chainId": "respondToChange",
        "parameters": {
          "text": "{{ $event.detail.value }}"
        }
      }
    ]
  }
}

Component event listeners are called in the same way as page lifecycle event listeners. There can be more than one listener. When there is more than one, they run in parallel. 

To reference an event listener from a component, you can use the $listeners.eventListenerName implicit object. For example:

<oj-select-single ... on-selection-change="[[$listeners.onSelectionChange]]"

Component Event Objects

Within the context of component event listeners, there are three implicit objects.

  • $event: The event payload sent by the component.

  • $current: This represents the second parameter passed to the handler, if any. For JET, this can be either the $current binding variable, or the $data variable if $current does not exist in the component context.

  • $bindingContext: represents the third parameter passed, if any. For JET, this is the (Knockout) view model, and it will therefore contain the $current or $data variable as a property.

These variables do not exist outside the listener context. In other words, you can reference these in the listener declaration, but you cannot reference them in the called action chain; any values needed in these variables must be passed explicitly to the action chain as arguments (chain variables).

These three variables represent the arguments passed to the listener, and are not directly tied to specific JET values. Their meaning could be different depending on the context.

For example, if using an event listener within an <oj-list-item> item, the value of $current could be different whether you are using the item.renderer attribute or the itemTemplate slot to display the item.

  • Within an item.renderer script, JET does not define $current, so instead passes $data as the second argument, so the Visual Builder $current is JET/Knockout $data. In some JET contexts, like anitem.renderer script, you will also need to prefix Visual Builder listeners with (Knockout) $parent in the HTML.

  • Within an itemTemplate slot, JET defines $current, and passes that, so Visual Builder $current is JET $current.

To determine whether JET $current exists for your use case., refer to the JET documentation for the component to which you are adding a listener.

Additionally, the developer could decide to pass their own custom object for the parameters. In the example below, the listener is wrapped, so Visual Builder $current is "some string", and Visual Builder $bindingContext is undefined.

<oj-button on-click="{{ function(event, current, bindingContext) { $page.listeners.someListener(event, "some string") } }}">
  Click Me!
</oj-button>

Component Event Listener "preventDefault" Property

Component event listeners have an additional preventDefault property, which can be used to prevent the normal DOM event handling from being executed.

This example uses an expression to check the payload of the event to stop propagation:

"eventListeners": {
  "customEventTwo": {
      "chains": [
        {
          "actionsId": "handleEventInMod2PageChain",
          "parameters": {
            "eventPayload": "{{ $event }}"
          },
        }
      ],
      "preventDefault": "{{ $event.type === 'info' }}"
  }

Component Event Listener "asyncBehavior" Property

Some components such as the JET table support events that accept async event listeners, where the event accepts a Promise. This allows the component that fired the event to cancel it asynchronously, if needed. The Promise provided by Visual Builder event listeners can also be resolved or rejected within Visual Builder based on the action chain's behavior.

To opt in to the async behavior for a component event, the eventListeners property asyncBehavior must be set to "enabled". The default value for this property is "disabled". Before implementing action chain logic, refer to the component docs to make sure you understand the implications of enabling async behavior.

Here's an example of enabling async behavior for a table component's ojBeforeRowEditEnd event, with the asyncBehavior property set to "enabled" within the eventListeners property:
{
  "eventListeners": {
    "tableBeforeRowEdit": {
      "asyncBehavior": "enabled",
      "chains": [
        {
          "chainId": "beforeRowEditChain",
          "parameters": {
            "rowIndex": "{{$event.detail.rowContext.status.rowIndex}}"
          }
        }
      ]
    }
  }
}
The table component bound to the ojBeforeRowEditEnd event in the preceding example can be configured as:
<oj-table scroll-policy="loadMoreOnScroll"
          id="oj-table-1"
          class="oj-flex-item oj-sm-12 oj-md-12"
          edit-mode="rowEdit"
          selection-mode='{"row": "single"}'
          data="{{ $page.variables.productsADP }}"
          scroll-policy-options.fetch-size="3"
          columns="{{ $page.functions.columnsArray }}"
            
          on-oj-before-row-edit="[[$listeners.table1BeforeRowEdit]]">
  ...
</oj-table>