# Custom component - CustomComponent

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

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

A 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 navigating to the **Library** module and selecting the **Custom components** tab, click the **Add custom component** button and in the opened window set the name and location of the created artifact (More information about the editor itself: [Custom Components 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>Illustration 1.</strong> Edit view of a newly created component</em></p></figcaption></figure>

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

Each CustomComponent consists of a single main function to which the component **Scope** is passed:

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

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

* **afterViewInit** - executed when the component is added to the application's DOM structure,
* **onModelChange** - executed when the component's model (e.g., value) or input values change,
* **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's ID in the application's DOM structure
  * **componentId** - the component ID&#x20;
  * **componentMid**  - the component MID
  * **visible** - variable controlling the component's visibility
  * **translations** - map of translations defined at component creation stage
  * **componentId** - map of component input parameters
  * **componentData** - map of the component's internal data (allows preserving the component's state)
  * **componentInput** - map of the component's input data (keys are defined in the **Inputs** section, separated by commas)&#x20;
* methods:
  * getValue() - retrieves the component's current value
  * setValue(value) - sets the component's current value
  * querySelector(query) - returns the DOM element of the component that matched the selector.
  * putData(key, value) - sets a specified value (value) under the key (key) in the component data map - client-side only in the browser
  * saveData(key, value) - sets a specified value (value) under the key (key) in the component data map - also on the server side
  * getData(key) - retrieves the value stored under the key (key) in the component data map
  * translate(key) - returns the translation from the translations map
  * clean() - cleans the scope object when it is no longer used by the CustomComponent
  * callServiceProxy(serviceName, params) - allows calling ServiceProxy and ScriptService services synchronously
  * callServiceProxyAsync(serviceName, params) - allows calling ServiceProxy and ScriptService services asynchronously
  * initTooltip(id, element, data) - allows initializing a tooltip with the given identifier (required value, must meet all HTML ID attribute requirements) on the provided 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 the given component
  * goForward() - navigates to the next (allowed) page or submits the application (when the user is on the last page of the application)
  * goBackward() - navigates to the previous (allowed) page&#x20;
  * setInactiveForward() - sets the button that navigates to the next page to locked (The availability of functionality depends on the license and may not be available in all deployments.)
  * setActiveForward() - sets the button that navigates to the next page to active (The availability of functionality depends on the license and may not be available in all deployments.)
  * triggerCustomEvent(eventName) - allows invoking a CustomEvent previously defined for the given component
  * sendCurrentValueEvent() - sends an event containing the current value of the CustomComponent along 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 - should not be overridden
    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 presenting a popup on the application with information about a positive credit decision was defined:

<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>Illustration 2.</strong></em> <em>Popup designed using a CustomComponent</em></p></figcaption></figure>

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

DOM structure of the popup:

{% 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 within a CustomComponent you can call ServiceProxy and ScriptService services using the methods *callServiceProxy* and *callServiceProxyAsync.* Both methods return an *Observable* with a response in the following format:

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

To obtain the *response* you should call the *.subscribe()* method known from RxJS. As in RxJS, it is also advisable to call *unsubscribe()* at the appropriate time on the returned object. For example, *unsubscribe* can be invoked in the *onDestroy*

method.

<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>When using ServiceProxy or ScriptService, it should be added to the list of used services on the CustomComponent:</strong></em> <em>Illustration 3.</em></p></figcaption></figure>

## "Used services" section <a href="#komponentniestandardowycustomcomponent-polawejsciowekomponentu" id="komponentniestandardowycustomcomponent-polawejsciowekomponentu"></a>

Component input fields **In the** Input data

<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>section the ids of the component input fields are defined. Ids should not contain spaces.</strong></em> <em>Illustration 4.</em></p></figcaption></figure>

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

<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>INPUT PARAMETERS</strong> section</em></p></figcaption></figure>

## Illustration 5.

Example of the "Input parameters" section for a CustomComponent added to the application **Translations**Dedicated internationalized texts are defined in the **Illustration 5.**.

<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>Component translations</strong></em> <em>section, which is displayed after selecting the button in the left panel</em></p></figcaption></figure>

Illustration 6a.

"Component translations" section

1. In this view the key and the default value of the text used on the CustomComponent are defined. Translations of the text into required languages are defined in the standard way on the application where the component was attached. **Example of adding a translation key:** After clicking the

   <figure><img src="https://2112972046-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2CssJT0zIo4SJQLbSZ6l%2Fuploads%2Fmy3y9jUNWC5UxgIps3Ym%2Fimage.png?alt=media&#x26;token=2034980c-9adf-4ede-8a69-fd46398be583" alt=""><figcaption><p><em><strong>Add translation</strong></em> <em>button at the bottom of the panel, you should add the translation key and the default value:</em></p></figcaption></figure>
2. Illustration 6b.

<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>Adding a new key and translation</strong></em> <em>The added translation key can be used in the CustomComponent's DOM structure:</em></p></figcaption></figure>

3. Illustration 6c. **Illustration 5.** "DOM" section with the example key "kg-final-survey-popup-title" entered<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>After adding the CustomComponent to the application or to a complex component in the</strong></em> <em>tab, the keys from the CustomComponent will appear. They can also be added manually.</em></p></figcaption></figure>

## Illustration 6d.

"Translations" tab with keys of the CustomComponent **Handling messages**For the CustomComponent to handle validation messages itself, you must check the option in the parameters section **Custom presentation of error message**.

<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>. This option allows handling messages via the</strong> onMessagesUpdate</em></p></figcaption></figure>

## method

Illustration 7.**The "Custom presentation of error message" option enabled**Defining custom events for the component

<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>For each CustomComponent you can define individual actions (</strong> CustomEvents</em></p></figcaption></figure>

). They are attached and handled in the same way as standard events defined on 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%2F0B3LiDaEB136FSMRcF21%2Fimage.png?alt=media&#x26;token=ae248ec1-7adc-4b85-93d5-bf79b51dd585" alt=""><figcaption><p><em><strong>Illustration 8.</strong> Actions added for the component</em></p></figcaption></figure>

Such defined actions can be attached on the application to a given action. For example, the TEST\_EVENT\_A action is used to open a popup: **Illustration 9.**&#x41;pplication properties view - "Actions" section with an action defined from the CustomComponent

```javascript
The event itself can be invoked inside the customComponent's JS script using the method
```

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

, for example: **$scope.triggerCustomEvent('TEST\_EVENT\_A');**&#x43;omponent simulation **JavaScript**, **To check the operation of the created CustomComponent you can use the simulation function. To do this, click the** Preview **button located on the right side of the screen. Clicking it changes the screen to simulation mode. On the left side we will see the**CSS

<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>and</strong> HTML</em></p></figcaption></figure>

panels, and on the right the component parameters (provided that input parameters have been defined). After launching the preview we can feed the component with variables available in the component and observe its behavior without having to embed it in the application and run it in the development environment. **Illustration 10.**&#x43;omponent view with preview enabled, without filling in input parameters **If you want changes in the preview to appear in real time, before filling in the component parameter fields it is worth checking the**Auto refresh **option. After filling in all fields click the**.

<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>Refresh</strong> button. Then the fields with entered values are hidden and the component appearance is shown. You can always display the entered parameters by clicking the option with the number of parameters and the label</em></p></figcaption></figure>

(show) **$scope.triggerCustomEvent('TEST\_EVENT\_A');** Illustration 11.

## Component view with simulation

Clicking the button again **will return us to the standard component view with the list of parameters on the left side.** Embedding in the application **The created CustomComponent is embedded in the application/complex component by adding it from the component palette. To do this, click the**Add component ![](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)button in the left side panel and in the expanded components panel select the

<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>Custom</strong></em> <em>tab, which is available after clicking the</em></p></figcaption></figure>

\
symbol. A list of CustomComponents available in the repository will be displayed. The top of the panel contains a search field that allows you to quickly find the artifact to be embedded. Adding a component involves dragging it from the palette and dropping it in the appropriate place on the application. **Illustration 12.** **List of components after clicking the Custom tab**) After embedding the component in the application, you must populate the component's input fields (defined according to the **Input fields** **section of the component) by clicking in the** Basic **- by mapping the appropriate data in the**:

<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>properties</strong></em> <em>option</em></p></figcaption></figure>

## Illustration 13.

Window for defining CustomComponent input parameters

* **Controlling the activity of the button that navigates to the next page** In the JavaScript tab of the edited CustomComponent we can use the methods:&#x20;
* **setInactiveForward()**- sets the button that navigates to the next page to locked&#x20;

setActiveForward **() - sets the button that navigates to the next page to active** Keep in mind that calling the **afterViewInit** setInactiveForward&#x20;

method inside  ***will cause the button to be locked after every change in the component's visibility - so this should be taken into account in the implementation.*** Controlling the button's activity can be conditioned on the channel in which the application is displayed using the conditional statement **if** Preview **, taking into account the session variables** channel

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

## channelDescription

(remember to map them to the component beforehand). **Calling the native API**The $scope object provides the

```
function($scope) {
    $scope.afterViewInit = function() {
        callNative
    }
}
```

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

## function, which accepts a function whose parameter is the native API object (signature: callNative(nativeFunction: (api: NativeAppApi) => any): void). The api object has an interface defined by the eximee platform. The function requested on the native object will be called only if that object is available in the context. <a href="#komponentniestandardowycustomcomponent-sterowanieloaderemplatformowym" id="komponentniestandardowycustomcomponent-sterowanieloaderemplatformowym"></a>

$scope.callNative(nativeApi => nativeApi.setTitle('title'));

* Controlling the platform loader
* The window object provides methods to control the platform loader:
