# External WebComponent

{% hint style="info" %}
Dostępność funkcjonalności zależy od licencji i może nie być dostępna we wszystkich wdrożeniach.
{% endhint %}

ExternalWebComponent jest wyspecjalizowanym komponentem, umożliwiającym tworzenie kontrolek spoza zestawu dostępnego na palecie. Wystarczy napisać własny WebComponent korzystając z języka HTML i JavaScript oraz zwrócić namiary na plik z taką zawartością za pomocą usługi (ScriptService lub ServiceProxy).

## Parametry wejściowe komponentu

<table><thead><tr><th width="213.199951171875">Parametr</th><th>Opis</th></tr></thead><tbody><tr><td>componentUrl</td><td>pełen link do źródła WebComponentu</td></tr><tr><td>tag</td><td>znacznik, jakim ma się identyfikować</td></tr><tr><td>inputs</td><td>lista parametrów wejściowych dla Componentu jako JSON</td></tr></tbody></table>

## Lista parametrów modelu wejściowego do WebComponentu <a href="#externalwebcomponent-listaparametrowmodeluwejsciowegodowebcomponentu" id="externalwebcomponent-listaparametrowmodeluwejsciowegodowebcomponentu"></a>

| Parametr wejściowy | Możliwa wartość      | Opis                                          |
| ------------------ | -------------------- | --------------------------------------------- |
| name               | \*                   | Nazwa parametru                               |
| value              | \*                   | Stała wartość lub nazwa komponentu na wniosku |
| type               | CONSTANT / FIELD     | Typ przekazywanej wartości                    |
| deliveryType       | ATTRIBUTE / PROPERTY | Sposób dostarczenia wartości do WebComponentu |

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

* Wszelkie parametry do komponentu (zdefiniowane w Eximee Designer) są przekazywane do komponentu za pomocą atrybutów. Jeżeli z serwera otrzymamy nową wartość, wartość atrybutu zostanie zmieniona.
* W przypadku pustych wartości atrybuty nie będą tworzone.
* Jeżeli update wartości wyczyści wartość danego atrybutu, to platforma usunie ten atrybut z elementu.
* Komponent może korzystać z dowolnych metod wystawionych przez API Eximee w obiekcie Window

  ```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;
  }
  ```
* Komponent może zmienić własną wartość emitując event **change** z polem **details** zawierającym obiekt spełniający interfejs `{ value: string }`. Można to uzyskać na przykład tworząc obiekt typu `CustomEvent` (np: `new CustomEvent('change', {detail: {value: event.target.value}})`). Dodatkowo komponent może wyemitować eventy **focus** oraz **blur** - nie będą one miały wpływu na logikę wniosku, ale zostaną odnotowane w statystykach. UWAGA! Kod powinien respektować fakt usunięcia komponentu z DOM - wszelkie eventy wysyłane po usunięciu komponentu mogą uniemożliwić złożenie wniosku.
* Poza zdefiniowanymi atrybutami platforma ustawi na komponencie następujące atrybuty:
  * **id** - identyfikator elementu w strukturze DOM. Platforma zapewnia jego unikalność i zgodność ze specyfikacją HTML
  * **component-id** - identyfikator komponentu - tego identyfikatora należy używać podczas komunikacji z platformą
  * **value** - aktualna wartość komponentu
  * **translations** - JSON z tłumaczeniami (klucz-wartość) dla literałów w WebComponencie
* Atrybut może być ustawiony przez platformę po connectedCallback - trzeba wpiąć się w attributeChangedCallback

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

Stała konfiguracja może wyglądać następująco:

```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" %}
W przypadku podania wartości @GesTextField1 oraz typu FIELD, platforma odpowiednio rozwiąże zagnieżdżone komponenty.
{% endhint %}

Można również podpiąć ServiceProxy (ScriptService), który zwróci powyższe dane.

```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" %}
Podpięcie EDS (źródła danych zewnętrznych) do ExternalWebComponentu spowoduje nadpisanie wszystkich pierwotnych wartości.

Oznacza to, że trzeba dostarczyć wszystkie parametry wejściowe (url, tag, inputs).
{% endhint %}

## Przykładowy 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 %}
