# 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 %}


---

# 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/budowanie-aplikacji/interfejs-uzytkownika/komponenty-rozszerzone/external-webcomponent.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.
