# Custom component - CustomComponent

{% hint style="warning" %}
Availability of the functionality depends on the license and may not be available in all implementations.
{% endhint %}

The custom component (CustomComponent) allows you to create your own components for the application using the language **JavaScript**.

CustomComponent consists of eight elements:

* JavaScript script,
* component CSS styles,
* component HTML template,
* mappings of the component's input parameters,
* list of used services,
* list of actions available for this component,
* translations of texts for the component,
* preview.

## Creating a component <a href="#komponentniestandardowycustomcomponent-tworzeniekomponentu" id="komponentniestandardowycustomcomponent-tworzeniekomponentu"></a>

A new component is created in the same way as other components. After going to the module **Library** and selecting the tab **Custom components** we click the button **Add custom component** and in the opened window we set the name and location of the created artifact (More information about the editor itself: [Custom component editor](https://docs.eximee.com/documentation/documentation-en/budowanie-aplikacji/interfejs-uzytkownika/komponenty-rozszerzone/komponent-niestandardowy-customcomponent/edytor-komponentow-niestandardowych))

<figure><img src="https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2F5XKDHhiqh9TVb5pV12ub%2Fimage.png?alt=media&#x26;token=605edb4d-ee2a-41e2-b749-6449256fac73" alt=""><figcaption><p><em><strong>Figure 1.</strong> Edit view of the newly created component</em></p></figcaption></figure>

## JavaScript script <a href="#komponentniestandardowycustomcomponent-skryptjavascript" id="komponentniestandardowycustomcomponent-skryptjavascript"></a>

Each of the CustomComponents consists of one main function, to which the **Scope** of the component is passed:

```
function($scope) {
  
}
```

Using the scope, the creator can, among other things, retrieve component data, input data, and react to certain events from the component life cycle. The user can implement three system methods of the component:

* **afterViewInit** - executed when the component is added to the application's DOM structure,
* **onModelChange** - executed when the model changes (e.g. the component value) or the input data value,
* **onDestroy** - executed when the component is removed from the application's DOM structure.

Additionally, the component scope provides several useful elements:

* fields:
  * **domId** - the component ID in the application's DOM structure
  * **componentId** - component ID
  * **componentMid** - component MID
  * **visible** - variable controlling the component's visibility
  * **translations** - map of translations defined during component creation
  * **componentId** - map of component input parameters
  * **componentData** - map of the component's internal data (allows the component state to be preserved)
  * **componentInput** - map of the component's input data (keys are defined in the section **Inputs** separated by commas)
* methods:
  * getValue() - gets the current value of the component
  * setValue(value) - sets the current value of the component
  * querySelector(query) - returns the DOM structure element of the component that matched the selector.
  * putData(key, value) - sets the specified value (value) under the key (key) in the component data map - only on the browser client side
  * saveData(key, value) - sets the specified value (value) under the key (key) in the component data map - also on the server side
  * getData(key) - gets the value specified under the key (key) in the component data map
  * translate(key) - returns the translation from the translation map
  * clean() - clears the scope object when it is no longer used by the CustomComponent
  * callServiceProxy(serviceName, params) - allows synchronous calling of ServiceProxy and ScriptService services
  * callServiceProxyAsync(serviceName, params) - allows asynchronous calling of ServiceProxy and ScriptService services
  * initTooltip(id, element, data) - allows you to initialize a tooltip with the given identifier (required value, must satisfy all requirements of the ID attribute in HTML) on the passed element
  * showTooltip(id) - shows the tooltip with the given id
  * hideTooltip(id) - hides the tooltip with the given id
  * destroyTooltip(id) - destroys the tooltip with the given id
  * destroyAllTooltips() - destroys all tooltips of a given component
  * goForward() - navigates to the next (allowed) page or sends the application (when the user is on the last page of the application)
  * goBackward() - navigates to the previous (allowed) page
  * setInactiveForward() - sets the button navigating to the next page to disabled (Availability of the functionality depends on the license and may not be available in all deployments.)
  * setActiveForward() - sets the button navigating to the next page to active (Availability of the functionality depends on the license and may not be available in all deployments.)
  * triggerCustomEvent(eventName) - allows you to trigger a CustomEvent previously defined for a given component
  * sendCurrentValueEvent() - sends an event containing the current value of the CustomComponent together with its id
  * isPageValid() - returns information whether there are validation errors on the page

## Component interfaces

{% code expandable="true" %}

```tsx
interface ExCustomComponentScope {
    // Methods that can be provided
    onDestroy?: () => void;
    afterViewInit?: () => void;
    onModelChange?: () => void;
    onMessagesUpdate?: (messages: ExWidgetMessage[]) => void;
 
    // Fields provided by the platform
    domId: string;
    componentId: string;
    componentMid: string;
    translations: { [key: string]: string; };
    componentInput: { [key: string]: string[]; };
    componentData: { [key: string]: string; };
    serviceResponses: { [key: string]: { [key: string]: string }[] };
    visible: boolean;
 
    // Methods provided by the platform - do not override them
    callNative(nativeFunction: (api: NativeAppApi) => any): void
    getValue(): string;
    setValue(value: string): void;
    /**
     * @deprecated New CustomComponents should use querySelector
    */
    queryChild(query: string): JQuery;
    querySelector(query: string): Element;
    putData(key: string, value: string): void;
    saveData(key: string, value: string): void;
    getData(key: string): string;
    translate(key: string): string;
    schedule(func: () => any): void;
    clean(): void;
    callServiceProxy(serviceName: string, params: { [key: string]: string[] }): Observable<ExServiceProxyResponse>;
    callServiceProxyAsync(serviceName: string, params: { [key: string]: string[] }): Observable<ExServiceProxyResponse>;
    initTooltip(id: string, element: any, data: ExWidgetTooltip): void;
    destroyTooltip(id: string): void;
    showTooltip(id: string): void;
    hideTooltip(id: string): void;
    destroyAllTooltips(): void;
    goForward(): void;
    goBackward(): void;
    setActiveForward(): void;
    setInactiveForward(): void;
    triggerCustomEvent(event: string): void;
    sendCurrentValueEvent(): void;
    isPageValid(): Observable<boolean>;
}
 
interface ExServiceProxyResponse {
    name: string;
    componentId: string;
    response: { [key: string]: string }[];
}
 
interface ExWidgetTooltip {
    text: string;
    position: ExWidgetTooltipPosition;
    type: ExWidgetTooltipType;
    width?: number;
    height?: number;
    styleClasses?: string;
    onFocus?: boolean;
}
 
enum ExWidgetTooltipPosition {
    TOP,
    BOTTOM,
    LEFT,
    RIGHT,
    ADAPTIVE,
// Deprecated:
    LEFT_TOP,
    LEFT_BOTTOM,
    RIGHT_TOP,
    RIGHT_BOTTOM
}
 
enum ExWidgetTooltipType {
    HOVER,
    BUTTON
}
interface ExWidgetMessage {
    getMessage(): string;
    getType(): ExWidgetMessageType;
}
 
enum ExWidgetMessageType {
    VALIDATION_POPUP,
    VALIDATION,
    INFO,
    COMPONENT_INFO
}
```

{% endcode %}

## Example of a component function

{% code lineNumbers="true" %}

```javascript
function($scope) {
      $scope.closePopup = function() {
        $scope.queryChild('.kg-positive-decision-popup-wrapper').fadeOut(200);
        $scope.saveData('visible', 'false');
      };
      $scope.afterViewInit = function() {
        let visible = $scope.getData('visible');
        if(visible === 'false') {
          $scope.queryChild('.kg-positive-decision-popup-wrapper').hide();
        } else {
          $scope.closeButton = $scope.queryChild('.kg-positive-decision-popup-close-button');
          $scope.closeButton.on('click', $scope.closePopup);
          $scope.nextButton = $scope.queryChild('.kg-positive-decision-popup-next-button');
          $scope.nextButton.on('click', $scope.closePopup)
        }
      };
    }
```

{% endcode %}

In the above example, a component was defined that displays on the application a Popup with information about a positive credit decision:

<figure><img src="https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2FvzqJCrmb3w7VWP7O3yxG%2Fimage.png?alt=media&#x26;token=95bd540f-34ef-4dd3-b799-8bff8805935b" alt=""><figcaption><p><em><strong>Figure 2.</strong></em> <em>Popup designed using CustomComponent</em></p></figcaption></figure>

In line 6, the system function was defined *afterViewInit* initializing the buttons *X* and *NEXT,* as well as controlling the visibility of the window. In line 2, a function executed when one of the two buttons is clicked was defined.

Popup DOM structure:

{% code lineNumbers="true" %}

```html
<div class='kg-positive-decision-popup-wrapper'>
  <div class='kg-positive-decision-popup'>
    <div class='kg-positive-decision-popup-title-wrapper'>
        <div class='kg-positive-decision-popup-title'>
            _{kg.positive.decision.popup.title}
        </div>
        <button class='kg-positive-decision-popup-close-button'></button>
        <div class='clear'></div>
    </div>
    <div class='kg-positive-decision-popup-content-wrapper'>
        <div class='kg-positive-decision-popup-content-first-paragraph'>
            _{kg.positive.decision.popup.content.first.paragraph}
        </div>
        <div class='kg-positive-decision-popup-content-second-paragraph'>
            _{kg.positive.decision.popup.content.second.paragraph}
        </div>
    </div>
    <div class='kg-positive-decision-popup-next-button-wrapper'>
        <button class='kg-positive-decision-popup-next-button'>_{kg.positive.decision.popup.next.button}</button>
        <div class='clear'></div>
    </div>
  </div>
</div>
```

{% endcode %}

In the component template definition, you can refer to defined translations using the convention **\_{TRANSLATION\_KEY}** - example in line 5.

### **Calling scripts or ServiceProxy**

From the CustomComponent level, you can call ServiceProxy and ScriptService services using the methods *callServiceProxy* and *callServiceProxyAsync.* Both methods return *Observable* with a response in the following format:

```
{
    name: string;
    componentId: string;
    response: { [key: string]: string }[];
}
```

To get the *response* you need to call the *.subscribe()* method known from RxJS. As in RxJS, it is also worth calling *unsubscribe()* at the appropriate moment on the returned object. For example, *unsubscribe* may be called in the *onDestroy.*

If you use ServiceProxy or ScriptService, you need to add it to the list of used services on the CustomComponent:

<figure><img src="https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2FZIf9KbXuiykzc4nhUn4b%2Fimage.png?alt=media&#x26;token=f63e8331-eb30-4ab3-877b-c04f7d64d444" alt=""><figcaption><p><em><strong>Figure 3.</strong></em> <em>Section "Used services"</em></p></figcaption></figure>

## Component input fields <a href="#komponentniestandardowycustomcomponent-polawejsciowekomponentu" id="komponentniestandardowycustomcomponent-polawejsciowekomponentu"></a>

In the section **Input data** the ids of the component's input fields are defined. IDs should not contain spaces.

<figure><img src="https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2F3SUcLy28j2RcUa6HQxKY%2Fimage.png?alt=media&#x26;token=7b63a748-5e28-4c6f-9d42-acfc0d635972" alt=""><figcaption><p><em><strong>Figure 4.</strong></em> <em>Section "Input data"</em></p></figcaption></figure>

The defined input fields can be fed by other application components in the same way as [complex components (ComplexComponent)](https://docs.eximee.com/budowanie-aplikacji/interfejs-uzytkownika/komponenty-rozszerzone/komponenty-zlozone#parametry-wejsciowe) - by mapping the appropriate data in the section **INPUT PARAMETERS**:

<figure><img src="https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2FB514ZDvzuhGQh1VTtJqs%2Fimage.png?alt=media&#x26;token=e4075382-5273-4833-9caf-c11741aed541" alt=""><figcaption><p><em><strong>Figure 5.</strong> Example of the "Input parameters" section for a CustomComponent added to the application</em></p></figcaption></figure>

## Translations

Dedicated internationalized texts are defined in the section **Component translations**, which appears after selecting the button in the left panel **Translations**.

<figure><img src="https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2FpDGrOGTiU6Q9g7IPJcIH%2Fimage.png?alt=media&#x26;token=912499cf-6e84-4566-84a8-3f92f624ff0f" alt=""><figcaption><p><em><strong>Figure 6a.</strong></em> <em>Section "Component translations"</em></p></figcaption></figure>

In this view, the key and default value of the text handled in the CustomComponent are defined. Translations of the text in the required languages are defined in the standard way on the application to which the component has been attached.

Example of adding a translation key:

1. After clicking the button at the bottom of the panel **Add translation** you need to add the translation key and default value:

   <figure><img src="https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2FgKXasP77SV29LdCBFPFH%2Fimage.png?alt=media&#x26;token=3bd276d8-5683-478a-be0a-9e83939ce156" alt=""><figcaption><p><em><strong>Figure 6b.</strong></em> <em>Adding a new key and translation</em></p></figcaption></figure>
2. The added translation key can be used in the DOM structure of the CustomComponent:

<figure><img src="https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2F3ox2dYRyonlrJR2OeSZB%2Fobraz.png?alt=media&#x26;token=56b0d0e3-953d-4d89-89ec-7d716f859c73" alt=""><figcaption><p><em><strong>Figure 6c.</strong></em> <em>Section "DOM" with the example key "kg-final-survey-popup-title" entered</em></p></figcaption></figure>

3. After adding the CustomComponent to the application or to a complex component in the tab **Translations** keys from the CustomComponent will appear. We can also add them manually.<br>

<figure><img src="https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2F9Lpm5N6Hr5UiloRXd2AY%2Fimage.png?alt=media&#x26;token=eaa5a363-6bdc-489e-bdd5-70ac77f0927e" alt=""><figcaption><p><em><strong>Figure 6d.</strong></em> <em>Tab "Translations" with CustomComponent keys</em></p></figcaption></figure>

## Handling messages

For the CustomComponent to handle validation messages itself, in the parameters section you should select the option **Custom error message display**. This option allows message handling via the method **onMessagesUpdate**.

<figure><img src="https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2F7iRkKvav68CprpZiopn9%2Fimage.png?alt=media&#x26;token=777ab5a7-a2d2-4445-9441-81791d2a1806" alt=""><figcaption><p><em><strong>Figure 7.</strong> Enabled option "Custom error message display"</em></p></figcaption></figure>

## Definition of custom events for the component

For each CustomComponent, individual actions can be defined (**CustomEvents**). They are attached and handled in the same way as standard events defined in the platform. The action definition looks as follows:

<figure><img src="https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2FSJOF8PaiHmFh4nTMTz2A%2Fimage.png?alt=media&#x26;token=dc2d50fe-8938-4a12-a530-8009a31dc05e" alt=""><figcaption><p><em><strong>Figure 8.</strong> Actions added for the component</em></p></figcaption></figure>

The actions defined in this way can be attached on the application to a given action. For example, the TEST\_EVENT\_A action is used to open a popup:

<figure><img src="https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2F0B3LiDaEB136FSMRcF21%2Fimage.png?alt=media&#x26;token=ae248ec1-7adc-4b85-93d5-bf79b51dd585" alt=""><figcaption><p><em><strong>Figure 9.</strong> Application properties view - section "Actions" with a defined action from the CustomComponent</em></p></figcaption></figure>

The event itself can be called inside the customComponent JS script using the method **triggerCustomEvent**, for example:

```javascript
$scope.triggerCustomEvent('TEST_EVENT_A');
```

## Component simulation <a href="#komponentniestandardowycustomcomponent-symulacjakomponentu" id="komponentniestandardowycustomcomponent-symulacjakomponentu"></a>

To check the operation of the created CustomComponent, you can use the simulation function.\
To do this, click the button located on the right side of the screen **Preview**. Clicking it changes the screen to simulation mode. On the left side we will see windows **JavaScript**, **CSS** and **HTML**, and on the right the component parameters (provided that input parameters have been defined). After starting the preview, we can feed the component with variables retrieved in the component and observe its behavior without having to embed it in the application and run it in a development environment.

<figure><img src="https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2FC3bZLbEkS0gNE3fi8Pak%2Fimage.png?alt=media&#x26;token=10dc8c12-aaf9-4d3f-ae6b-11ecf2e342b6" alt=""><figcaption><p><em><strong>Figure 10.</strong> View of the component with preview enabled, without filling in the input parameters</em></p></figcaption></figure>

If we want changes in the preview to be shown live, before filling in the component parameter fields, it is worth selecting the option **Automatic refresh**. After filling in all fields, click the button **Refresh**. Then the fields with the entered values disappear, and the component appearance is shown. You can always display the filled-in parameters by clicking the option with the number of parameters and the word **(show)**.

<figure><img src="https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2FIrrMtVvgjuwuF8spdFCF%2Fimage.png?alt=media&#x26;token=21150ed5-4f51-4fd7-93f9-fc8896ba2557" alt=""><figcaption><p><em><strong>Figure 11.</strong> View of the component with simulation</em></p></figcaption></figure>

After clicking the button again **Preview** we will return to the standard component view with the list of parameters on the left side.

## Embedding on the application

The created CustomComponent is embedded in the application/in the complex component by adding it from the component palette. To do this, in the left side panel click the button **Add component** and in the slid-out components panel select the tab **Custom**, which is available after clicking the symbol ![](https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2FXpvGXQmoGnzv8PqfqsFN%2Fimage.png?alt=media\&token=bf402bc1-cab4-4256-8d54-9fc1f5100fca). A list of available CustomComponents in the repository will be displayed.\
At the top of the panel there is a search field allowing you to quickly find the artifact to embed. Adding a component consists of dragging it from the palette and dropping it in the appropriate place on the application.

<figure><img src="https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2FvuGLqLYDVuNRo7RfmzsM%2Fimage.png?alt=media&#x26;token=54006942-3d8d-43a3-97ab-68f72851c9a6" alt=""><figcaption><p><em><strong>Figure 12.</strong></em> <em>List of components after clicking the Custom tab</em></p></figcaption></figure>

\
After embedding the component in the application, the component's input fields should be filled (defined according to the section **Input fields** **of the component**) by clicking in the section **Basic** **properties** field the **INPUT PARAMETERS**:

<figure><img src="https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2FZXwJb1BKhLhBpG1LNnMK%2Fimage.png?alt=media&#x26;token=ea6861e9-ea7c-4059-8d58-1c378e28eccc" alt=""><figcaption><p><em><strong>Figure 13.</strong></em> <em>Window for defining CustomComponent input parameters</em></p></figcaption></figure>

## Controlling the activity of the button navigating to the next page

In the JavaScript tab of the edited CustomComponent we can use the methods:

* **setInactiveForward()** - sets the button navigating to the next page to disabled
* **setActiveForward**() - sets the button navigating to the next page to active

Remember that calling the method **setInactiveForward** inside **afterViewInit** will disable the button after each change in the component's visibility - this should therefore be taken into account in the implementation.

The activity of the button can be made dependent on the channel in which the application is displayed using the conditional statement ***if*** , taking into account the session variables **channel** and **channelDescritpion** (remember to map them to the component beforehand).

{% hint style="warning" %}
Availability of the functionality depends on the license and may not be available in all implementations.
{% endhint %}

## Calling the native API

The $scope object provides the function **callNative**, which takes a function whose parameter is the native API object (signature: callNative(nativeFunction: (api: NativeAppApi) => any): void). The api object has a defined interface provided by the eximee platform. The function requested on the native object will be called only if this object is available in the context.

```
function($scope) {
    $scope.afterViewInit = function() {
        $scope.callNative(nativeApi => nativeApi.setTitle('title'));
    }
}
```

{% hint style="info" %}
Availability of the functionality depends on the license and may not be available in all implementations.
{% endhint %}

## Controlling the platform loader <a href="#komponentniestandardowycustomcomponent-sterowanieloaderemplatformowym" id="komponentniestandardowycustomcomponent-sterowanieloaderemplatformowym"></a>

The window object has methods available that allow controlling the platform loader:

* startSpinner() - turns on the loader
* stopSpinner() - turns off the loader


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.eximee.com/documentation/documentation-en/budowanie-aplikacji/interfejs-uzytkownika/komponenty-rozszerzone/komponent-niestandardowy-customcomponent.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
