> For the complete documentation index, see [llms.txt](https://docs.eximee.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.eximee.com/documentation/documentation-en/wprowadzenie/architektura-platformy/moduly-wykonawcze/eximee-forms/osadzanie-eximee-forms-jako-webcomponent.md).

# Embedding Eximee Forms as a web component

The Eximee platform Forms presentation module can be launched in the variant:

* a standalone single page application hosted as a dedicated website, embedded within a webview or iframe,
* a web component library to be embedded in any existing website or web application.

The forms application is functionally responsible for:

* presenting and handling forms defined using low-code in the Eximee platform,
* each form has a dynamic structure consisting of components and one or more pages defined using low-code,
* Eximee forms manage the form state, handle user interactions and navigation within the displayed form,
* the application can use browser URL navigation, using exclusively the part after #, or in-memory navigation that does not affect the browser URL state.

The documentation below describes how to embed and integrate Eximee forms using the web component library.

## General assumptions

To launch a form:

* JavaScript files providing component implementations need to be included on the website,
  * component assets are served by the Eximee platform in a version compatible with the platform server,
  * the specific names and addresses of the assets result from the manifest hosted together with the other static files of the platform,
* ensure access to the Eximee platform REST API from the host application's domain,
* ensure correct CORS and CSP headers,
* create the component DOM element using HTML or programmatically using JavaScript,
* initiate form loading using the programmatic API from JavaScript.

## Technologies used

The application is built using Angular version 20.x.x (subject to regular updates).

And it exposes a web component according to the web component specification in the area of:

* custom elements,
* shadow DOM.

The web component can be embedded inside an open Shadow DOM, however this must be taken into account when embedding styles from the bundleStats.json manifest into the DOM (described below).

## Impact on the application's global execution context by zone.js

The application depends on the availability of a globally loaded zone.js library in a version compatible with Angular (and delivered together with Angular/Angular CLI).

The zone.js library is the foundation of the Angular framework and is widely used in applications built with it.

The library works by monkey-patching asynchronous APIs for user interaction with the page in order to support UI change detection during user interactions.

The Eximee forms application is currently not adapted to work in zoneless mode, and any adaptation would require dedicated work on the Eximee platform side.

However, previous experience shows that in the case of:

* using the web component inside a host application that uses a compatible version of zone.js (e.g. written in Angular),
* using the web component inside a host application that does not use zone.js (e.g. written in Vue or React).

We have not observed conflicts or problems with the operation of either of these applications (host, Eximee). However, it should be noted that the compatibility of libraries embedded in the running application must be verified.

## Impact on the application's global execution context by polyfills

The web component library depends on the availability of polyfills (and includes them if they are not available):

* core-js/shim from core-js,
* @webcomponents/webcomponentsjs/custom-elements-es5-adapter.js from @webcomponents/webcomponentsjs,
* web-components/webcomponents-loader from polymer.

All polyfill dependencies comply with polyfill delivery standards, i.e.

* they do not override native solutions available in the browser,
* they load only if they provide functionality not available natively and not provided by other methods (e.g. by the host application),
* they are provided by commonly used open source libraries.

In practice, the specified polyfills should not change behavior in the case of:

* using modern browsers,
* using a host application that uses modern web frameworks (such as Angular).

## Resource manifest and release cycle

The web component library assets depend on the current version of the Eximee platform embedded in a specific environment.

To make dependency management easier, the platform hosts a manifest file describing the assets required to be included on the page in order to launch the web component.

The manifest is in JSON format and looks like this:

```json
{
  "format": 3,
  "scripts": [
    "polyfills.125595b8f8d58bce.js",
    "main.922b3d09b697675a.js"
  ],
  "globalStyles": [
    "global-styles.a9c4b7e18d2f03ab.css"
  ],
  "styles": [
    "styles.6e50ddf23fe0e270.css"
  ]
}
```

The manifest contains three types of assets, which must be loaded in different ways:

* scripts – JavaScript files; they should be embedded in `<head>` of the page hosting the web component (in tags `<script>`),
* globalStyles – global style sheets; they should be embedded in `<head>` of the page hosting the web component (in tags `<link>`). They contain only assets unrelated to layout or the appearance of DOM elements, such as font definitions (`@font-face`). They will not affect the styling of elements in the existing host application,
* styles – component-specific style sheets; they should be attached directly to the component's DOM element (or its shadowRoot, if the web component is embedded in Shadow DOM).

All asset names contain hashes based on file contents. This makes it possible to simultaneously:

* ensure that the correct file is loaded in accordance with the specific version of the system,
* reuse files from cache if their contents do not change between versions.

Asset embedding can be done:

* dynamically by the application's frontend before the form is launched,
* as part of server-side rendering of the page hosting the host application (recommended method).

Note: if the form web component is embedded within nodes behind an open shadow DOM, it is necessary to embed style links inside the appropriate shadowRoot. Styles embedded in the head of the entire document will not be able to style the component inside the shadow DOM in the standard way.

## Creating a form instance

### Embedding the component and launching the form

The component can be embedded in the DOM using the component's HTML tag or programmatically using the JavaScript API:

```typescript
var form = document.createElement("ex-forms-form");
document.body.appendChild(form)
```

The form component can be embedded anywhere in the application's DOM structure.

After embedding and obtaining a reference to the element, it is possible to launch the form using the loadForm method according to the example:

```typescript
container.loadForm({
  formId: 'demoFormularzJakoWebcomponent', /* form identifier */
  baseHref: '/api',  /* path where the Eximee REST API exposed by proxy-pass is available */
  onError: function () {
    alert('An error occurred while processing the application');
  }
});
```

It is possible to pass additional parameters to the load form method, which will be described in the next sections of the documentation and provided in the reference API at the end of the document.

One such parameter is the ability to pass business parameters for launching a specific form, for example:

```typescript
container.loadForm({
  formId: 'demoFormularzJakoWebcomponent', /* form identifier */
  baseHref: '/api',  /* path where the Eximee REST API exposed by proxy-pass is available */
  data: JSON.stringify({'param1': 'value1'}), /* Additional business parameters feeding the form in the form of a key-value object serialized to JSON string  */
  onError: function () {
    alert('An error occurred while processing the application');
  }
});
```

### Extending REST communication headers (including auth headers for API Gateway)

In many deployments there is a need to extend REST API request headers, especially in applications that handle user authentication and rely on access control at the API Gateway level (e.g. OIDC tokens).

For this purpose it is possible to specify a function that creates the headers that will be attached to each REST request:

```typescript
form.loadForm({
    formId: 'demoFormularzJakoWebcomponent',
    additionalRequestHeaders: () => ({
        'X-Custom-Header': 'value'
    })
})
```

The header-creating method is called every time, immediately before sending a request to the REST API. Headers are not stored between calls, which is especially important, for example, for OAuth headers that may change as a result of token refresh during form handling.

### Preserving form state between page refreshes / host application navigation

The complete form state and user-entered data is stored on the Eximee platform server side within the user session.

This means that it is possible to recreate/resume the user's active form even after navigation in the host application or a full page refresh in the browser.

The user's form on the server side is distinguished based on:

* the session identifier in the Cookie,
* the form instance identifier in the session based on the form instance number (formInstanceNumber).

Assuming that Cookie handling is guaranteed and the Cookie will not be deleted (except when functional requirements require such deletion), then to restore the form it is necessary to store its instance number.

The form number can be passed to the load form method and retrieved from the instance after it is launched. Assuming that the host application has a unique way of storing this value (e.g. in the page query, server-side, etc.), it is possible to write:

```typescript
let formInstanceNumber: string | undefined = restoreFormInstanceNumber();
form.loadForm({
    formId: 'demoFormularzJakoWebcomponent',
    formInstanceNumber: formInstanceNumber,
    onLoaded: (result, config) => storeFormInstanceNumber(result.data.formModel.formNumber),
});

```

## Access to the Eximee platform REST infrastructure

The component presenting forms requires access to the REST API served by the Eximee platform instance. All REST API endpoints are already hosted and exposed for standalone webforms instances (e.g. for web applications, webview or iframe embedding).

Communication endpoints must be available through a proxy in the host application's domain passing traffic to the Eximee infrastructure. Communication between different application domains and Eximee is not possible due to limitations in managing third-party cookies in browsers and the lack of inter-module web worker support.

It is also possible to develop another communication mechanism, especially one in which the host application mediates every REST call. However, this requires analysis and the development of a solution specific to the given deployment and involves planning additional development work in the platform.

## Form application cookie

The application uses cookies describing:

* the user session,
* session affinity parameters for load balancers.

Cookies are created automatically by the server and infrastructure (load balancers) and are configured according to the parameters of a specific environment.

## Removing the component

To safely remove the component, before removing it from the DOM you should call the asynchronous method exposed on the web component's DOM element `destroy`.

## Known functional limitations

* The library assumes that only one form is displayed on a single screen at a time, and attempting to display two concurrently running form instances may cause errors.
* Changing the parameters of an initialized form requires reinitializing it and means preparing a new instance (without the data previously entered by the user).

## Component interface

```typescript
export interface FormWebcomponentApi {
    loadForm(config: LoadFormConfig): void;
    hasActiveForm(): boolean;
 
    cancelCurrentForm(): void;
    handleAction(action: ExAction): void;
    proceedCurrentForm(): void;
    backCurrentForm(): void;
    isLastVisiblePage(): boolean;
    isFirstVisiblePage(): boolean;
    shouldShowForwardButton(): boolean;
    shouldShowBackwardButton(): boolean;
    getForwardButtonLabel(): string;
    onShowSpinner(callback: () => void): void;
    onHideSpinner(callback: () => void): void;
    getTranslation(key: string): string;
    destroy(): Promise<void>;
}
 
export interface LoadFormConfig extends LoadConfig {
    formId: string;
}
 
export interface LoadConfig {
    formInstanceNumber?: string;
    processId?: string;
    baseHref?: string;
    accessToken?: string;
    tokenType?: string;
    readonly?: boolean;
    data?: string;
    shadowRoot?: ShadowRoot;
    scrollOnErrorOffset?: number;
    additionalRequestHeaders?: () => { [header: string]: string };
    onLoaded?: (result: unknown, config: LoadConfig) => void;
    onActionDispatched?: (result: unknown, config: LoadConfig) => void;
    onCancelled?: (config: LoadConfig) => void;
    onSaved?: (result, config: LoadConfig) => void;
    onDraftSaved?: (result, config: LoadConfig) => void;
    onPageChanged?: (result, config: LoadConfig) => void;
    onModelChanged?: (result, config: LoadConfig) => void;
    onPageValidationErrors?: (result, config: LoadConfig) => void;
    onError?: (error, config: LoadConfig) => void;
    onAppEvent?: (event, config: LoadConfig) => void;
    onComponentValueChanged?: (result: unknown, config: LoadConfig) => void;
    onShowSpinner?: (immediate: boolean ) => void;
    onHideSpinner?: () => void;
    onClosed?: (config: LoadConfig) => void;
}
 
export interface ExAction {
    sourceId: string;
    event: ExActionEvent | string;
    detail?: object | string | number | boolean;
}
 
export enum ExActionEvent {
    SAVE = 'SAVE',
    CLOSE = 'CLOSE',
    NEXT = 'NEXT',
    EDIT = 'EDIT',
    CLICK_MORE_INFO = 'CLICK_MORE_INFO',
    CALL = 'CALL',
    CHECK = 'CHECK',
    UNCHECK = 'UNCHECK',
    CLICK = 'CLICK',
    ON_EXIT = 'ON_EXIT',
    TOOLTIP_CLICKED = 'TOOLTIP_CLICKED',
    AUTOCOMPLETE_NO_MATCH_BUTTON_CLICKED = 'AUTOCOMPLETE_NO_MATCH_BUTTON_CLICKED',
    EXPAND_STATEMENT = 'EXPAND_STATEMENT',
    ON_PAGE_ENTER = 'ON_PAGE_ENTER',
    POI_SELECTED = 'POI_SELECTED',
    HIDDEN = 'HIDDEN',
    CLOSE_POPUP = 'CLOSE_POPUP',
    RETRY = 'RETRY',
    SAVE_DRAFT = 'SAVE_DRAFT',
    VALUE_CHANGED = 'VALUE_CHANGED',
    FORWARD_PAGE = 'FORWARD_PAGE',
    BACKWARD_PAGE = 'BACKWARD_PAGE',
    PARK_FORM_WITH_PROVIDED_HASH = 'PARK_FORM_WITH_PROVIDED_HASH',
    SHOW_POPUP = 'SHOW_POPUP',
    SAVE_POPUP = 'SAVE_POPUP',
    TOGGLE = 'TOGGLE',
    CLEAR_UPLOAD_FILE = 'CLEAR_UPLOAD_FILE',
    REDIRECT_TO_RETURN_URL = 'REDIRECT_TO_RETURN_URL',
    REDIRECT = 'REDIRECT',
    CHECK_FED_STATEMENT = 'CHECK_FED_STATEMENT',
    POPUP_SAVED = 'POPUP_SAVED',
    POPUP_HIDDEN = 'POPUP_HIDDEN',
    START_PROCESS = 'START_PROCESS',
    COMPLETE_USER_TASK = 'COMPLETE_USER_TASK',
    TILE_CLICKED = 'TILE_CLICKED',
    START_APPLICATION = 'START_APPLICATION',
    EXIT_CONFIRMED = 'EXIT_CONFIRMED'
}
```

## Examples of use

The platform deployed on test environments hosts sample HTML embedding the form using the web component:

* directly in the page DOM,
* wrapped in a Shadow Root.

Knowing the Eximee platform address, both examples can be viewed at https\://\[environment-address]/webcomponent/\[deployment-theme]-webcomponent.html

HTML example:

```html
<html>
<head>
    <script>
        /*  Method for launching the application directly in the DOM tree. */
        function initFormPlain() {
            // Prepare component in the DOM tree
            const formWrapper = document.getElementById('form-wrapper');
            const formWebcomponent = document.createElement("ex-forms-form");
            formWrapper.appendChild(formWebcomponent);
 
            // Launch the form instance
            formWebcomponent.loadForm({
                formId: 'demoFormularzJakoWebcomponent',
                baseHref: '/api',
                onError: function () {
                    alert('An error occurred while processing the application');
                }
            });
        }
    </script>
</head>
<body>
    <div id="form-wrapper"></div>
    <script src="polyfills.5310c9e539f37fb1.js" type="module"></script>
    <script src="main.71e5244a2a4d4f3d.js" type="module"></script>
</body>
</html>
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.eximee.com/documentation/documentation-en/wprowadzenie/architektura-platformy/moduly-wykonawcze/eximee-forms/osadzanie-eximee-forms-jako-webcomponent.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
