# External WebComponent

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

ExternalWebComponent is a specialized component that enables creating controls outside the set available on the palette. It is enough to write your own WebComponent using HTML and JavaScript and provide the path to a file with such content via a service (ScriptService or ServiceProxy).

## Component input parameters

<table><thead><tr><th width="213.199951171875">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>componentUrl</td><td>full link to the WebComponent source</td></tr><tr><td>tag</td><td>the tag by which it should be identified</td></tr><tr><td>inputs</td><td>list of input parameters for the Component as JSON</td></tr></tbody></table>

## List of input model parameters for the WebComponent <a href="#externalwebcomponent-listaparametrowmodeluwejsciowegodowebcomponentu" id="externalwebcomponent-listaparametrowmodeluwejsciowegodowebcomponentu"></a>

| Input parameter | Possible value       | Description                                         |
| --------------- | -------------------- | --------------------------------------------------- |
| name            | \*                   | Parameter name                                      |
| value           | \*                   | Constant value or component name in the application |
| type            | CONSTANT / FIELD     | Type of the transmitted value                       |
| deliveryType    | ATTRIBUTE / PROPERTY | Method of delivering the value to the WebComponent  |

## Communication <a href="#externalwebcomponent-komunikacja" id="externalwebcomponent-komunikacja"></a>

* Any parameters for the component (defined in Eximee Designer) are passed to the component via attributes. If a new value is received from the server, the attribute value will be changed.
* In the case of empty values, attributes will not be created.
* If a value update clears the value of a given attribute, the platform will remove that attribute from the element.
* The component can use any methods exposed by the Eximee API in the Window object

  ```javascript
  export interface ExWebformsWindow extends Window {
      getComponentValue: (componentId: string) => unknown;
      setComponentValue: (sourceComponentId: string, componentId: string, newValue: string) => unknown;
      updateExternalSectionValue: (externalSectionId: string, newValue: string) => unknown;
      finishApplication: () => void;
      applicationSetTitle: () => void;
      goForward: () => void;
      goBackward: () => void;
      returnToOriginalForm: () => boolean;
  }
  ```
* The component can change its own value by emitting an event **change** with the field **details** containing an object that satisfies the interface `{ value: string }`. This can be obtained, for example, by creating an object of type `CustomEvent` (e.g.: `new CustomEvent('change', {detail: {value: event.target.value}})`). Additionally, the component can emit the events **focus** and **blur** - they will not affect the application logic, but will be recorded in statistics. NOTE! The code should respect the fact that the component is removed from the DOM - any events sent after the component is removed may prevent the application from being submitted.
* In addition to the defined attributes, the platform will set the following attributes on the component:
  * **id** - element identifier in the DOM structure. The platform ensures its uniqueness and compliance with the HTML specification
  * **component-id** - component identifier - this identifier should be used when communicating with the platform
  * **value** - current value of the component
  * **translations** - JSON with translations (key-value) for literals in the WebComponent
* The attribute may be set by the platform after connectedCallback - you need to hook into attributeChangedCallback

## Configuration <a href="#externalwebcomponent-konfiguracja" id="externalwebcomponent-konfiguracja"></a>

A constant configuration may look as follows:

```xml
<p1:GesExternalWebComponent id="GesExternalWebComponent2" mid="GesExternalWebComponent2" inheritLayout="false" tag="img-only" componentUrl="https://dev.bank.eximee.consdata.local/bank-demo-ewc/imgonly.js">
  <data:ListeningOn/>
  <data:ClearOn/>
  <p1:GesExternalWebComponent.layoutData>
    <ns6:GridData horizontalAlignment="FILL" horizontalSpan="17" verticalAlignment="CENTER"/>
  </p1:GesExternalWebComponent.layoutData>
  <data:ExternalWebComponentInputMapping>
    <data:ExternalWebComponentMapping value="some-value" name="name1" type="CONSTANT" deliveryType="ATTRIBUTE"/>
    <data:ExternalWebComponentMapping value="GesTextField1" name="name2" type="FIELD" deliveryType="PROPERTY"/>
  </data:ExternalWebComponentInputMapping>
</p1:GesExternalWebComponent>
```

{% hint style="warning" %}
When the value @GesTextField1 and type FIELD are provided, the platform will resolve nested components accordingly.
{% endhint %}

You can also attach a ServiceProxy (ScriptService), which will return the above data.

```xml
<p1:GesExternalWebComponent id="GesExternalWebComponent1" mid="GesExternalWebComponent1" inheritLayout="false">
  <data:ExternalDataSource name="demo_cross_sell_ewc" type="SCRIPT_SERVICE" changesWidgetAttributes="false" version="*">
    <data:OutputFields>
      <data:Field localName="componentUrl" serviceName="url_to_webcomponent"/>
      <data:Field localName="tag" serviceName="webcomponent_tag"/>
    </data:OutputFields>
  </data:ExternalDataSource>
  <data:ListeningOn/>
  <data:ClearOn/>
  <p1:GesExternalWebComponent.layoutData>
    <ns6:GridData horizontalAlignment="FILL" horizontalSpan="17" verticalAlignment="CENTER"/>
  </p1:GesExternalWebComponent.layoutData>
</p1:GesExternalWebComponent>
```

{% hint style="warning" %}
Attaching an EDS (external data source) to the ExternalWebComponent will overwrite all original values.

This means that all input parameters (url, tag, inputs) must be provided.
{% endhint %}

## Example WebComponent <a href="#externalwebcomponent-przykladowywebcomponent" id="externalwebcomponent-przykladowywebcomponent"></a>

{% code expandable="true" %}

```javascript
const imgTemplate = document.createElement('template');
imgTemplate.innerHTML = `
    <div class="wrapper">
        <img id="noAttributeAndProperty" src="https://prowly-uploads.s3.eu-west-1.amazonaws.com/uploads/4843/assets/191020/large-09ba51481c9c5dd157eea3438af2bcbf.png" width="300" height="350"/>
        <img id="falseAtribute" style="display: none" src="https://upload.wikimedia.org/wikipedia/en/1/14/WO%C5%9AP.svg" width="300" height="350"/>
        <img id="trueAtribute" style="display: none" src="https://www.wroclaw.pl/go/download/img-32cf4a052df7bd08570783a8351863eb/wosp-26-jpg.jpg" width="300" height="350"/>
        <img id="falseProperty" style="display: none" src="https://static.wosp.org.pl/trunk/uploaded/sended/files/programy/logo-rur-pl.png?1573732254027" width="300" height="350"/>
        <img id="trueProperty" style="display: none" src="https://admonkey.pl/wp-content/uploads/2017/01/logo_wosp.jpg" width="300" height="350"/>
    </div>`;
 
 
class ImgOnly extends HTMLElement {
 
    constructor() {
        super();
        this.shadow = this.attachShadow({
            mode: 'open'
        });
    }
 
    getAtributesAndProperties() {
        let atrGroup = null
        let propsGroup = null
        let element = document.getElementById('GesComplexComponent1_GesExternalWebComponent2-element')
        if (!!element) {
            atrGroup = element.getAttribute("atrgroup")
            propsGroup = element['propsGroup']
        }
        if ((atrGroup == null || atrGroup == undefined) && (propsGroup == null || propsGroup == undefined)) {
            this.shadow.getElementById("noAttributeAndProperty").style.display = "inline-block";
            return;
        } else {
            element.shadowRoot.getElementById("noAttributeAndProperty").style.display = "none";
        }
        if (atrGroup === "true") {
            element.shadowRoot.getElementById("trueAtribute").style.display = "inline-block";
            element.shadowRoot.getElementById("falseAtribute").style.display = "none";
        }
        if (atrGroup === "false") {
            element.shadowRoot.getElementById("falseAtribute").style.display = "inline-block";
            element.shadowRoot.getElementById("trueAtribute").style.display = "none";
        }
        if (propsGroup === "true") {
            element.shadowRoot.getElementById("trueProperty").style.display = "inline-block";
            element.shadowRoot.getElementById("falseProperty").style.display = "none";
        }
        if (propsGroup === "false") {
            element.shadowRoot.getElementById("falseProperty").style.display = "inline-block";
            element.shadowRoot.getElementById("trueProperty").style.display = "none";
        }
 
    }
 
    connectedCallback() {
        this.shadow.appendChild(imgTemplate.content.cloneNode(true));
        setInterval(this.getAtributesAndProperties, 500);
 
    }
 
    attributeChangedCallback(name, oldValue, newValue) {
        switch (name) {
            case 'atrgroup':
                this.getAtributesAndProperties();
                break;
        }
    }
 
 
    static get observedAttributes() {
        return ['atrgroup'];
    }
}
 
window.customElements.define('img-only', ImgOnly);
```

{% endcode %}
