JavaScript UI module

Developer productivity Unbundled: Extension Version 4.0.0 Magnolia 6.4 compatible

Issues

Git

Git

The JavaScript UI module lets you build custom apps, dialogs, and form fields with your own frontend, loaded from a module or an external URL, and embedded via an iframe. You can integrate any frontend technology you want, such as React, Vue, or Angular, and communicate with Magnolia using a message-based API. In 4.0+, a helper API (window.iframeClient.magnoliaAPI) is also available.

In Magnolia 6.4 and later, JavaScript fields are the supported way to build custom fields for Magnolia’s New UI Forms.

The module requires that you have a DX Core Enterprise license.

Installing with Maven

Maven is the easiest way to install the module. Add the following to your bundle:

  • The core dependency is required:

    <dependency>
      <groupId>info.magnolia.ui</groupId>
      <artifactId>magnolia-ui-framework-javascript-core</artifactId>
      <version>4.0.0</version>
    </dependency>
  • To use JavaScript form fields, include:

    <dependency>
      <groupId>info.magnolia.ui</groupId>
      <artifactId>magnolia-ui-framework-javascript-field</artifactId>
      <version>4.0.0</version>
    </dependency>
  • To integrate with the New UI Forms, include:

    <dependency>
      <groupId>info.magnolia.ui</groupId>
      <artifactId>magnolia-ui-framework-javascript-warp-integration</artifactId>
      <version>4.0.x</version>
    </dependency>
  • To use JavaScript subapps, include:

    <dependency>
      <groupId>info.magnolia.ui</groupId>
      <artifactId>magnolia-ui-framework-javascript-app</artifactId>
      <version>4.0.0</version>
    </dependency>
  • To use JavaScript dialogs, include:

    <dependency>
      <groupId>info.magnolia.ui</groupId>
      <artifactId>magnolia-ui-framework-javascript-dialog</artifactId>
      <version>4.0.0</version>
    </dependency>
  • To use Ecommerce-based dialogs, see the Ecommerce section.

Custom form fields

Custom form fields let you integrate any script to manage a fieldValue for a form. Define your own script file and implement the message communication pattern described below.

Configuration

Add a JavaScript field in your dialog definition as follows:

label: Home page
form:
  properties:
    colorField:
      label: Background color
      $type: javascriptField (1)
      fieldScript: /my-light-module/webresources/colorField/index.html (2)
      height: 70 (3)
      defaultValue: "#00ff00" (4)
      parameters: (5)
        foo: bar
1 Must be javascriptField.
2 Points to a valid HTML file located in:
3 The default height of the field in the dialog.
4 The field default value.
5 Pass custom parameters to the JavaScript field. The fieldScript receives these parameters in its init message, allowing you to use them in your script.

Before 4.x, you had to set:

implementationClass: info.magnolia.ui.javascript.form.FormViewWithChangeListener

In 4.x, if this class is still present, the form falls back to the legacy Vaadin forms.

We recommend removing the implementationClass so the form uses the New UI Forms (introduced in Magnolia 6.4) and JavaScript fields work correctly. A fix is planned to prevent this fallback: AIACC-346.

This does not apply to JavaScript dialogs, where implementationClass remains required.

Message format

When your custom form field loads, you must inject some basic logic to:

  • Integrate with the rest of the form.

  • Enable you to verify, change, and save form values.

  • Open dialogs.

  • Call REST endpoints.

  • Navigate to internal links.

Communication between Magnolia and your iframe uses the standard postMessage API. Magnolia dispatches message events that include an action field.

The fieldScript document needs to communicate with the form engine using JavaScript and event messaging. It should be able to receive information from the form engine. It must listen to at least init and may listen to other optional events such as change, restResponse, and dialogResponse.

The fieldScript document can also send information using the Helper API or with the standard messaging system.

Your iframe should register a message listener as shown below:

window.addEventListener(
  'message',
  function (event) {
    if (event.data.action === 'init') {
      console.log(event.data.state);
    }
    //Optional:  Our form state manager notifies any change to your iframe too. You can react to it as shown in the following example:
    if (event.data.action === 'change') {
        if (event.data.state && event.data.state.formFields) {
            // You can consider adding the state globally in your iframe context
            window.formFields = event.data.state.formFields;
            console.log('::: FormFields updated state', window.formFields);
        }
    }
    //Optional: If you requested a REST endpoint, the response is notified as this:
    if (event.data.action === 'restResponse') {
        console.log('REST Response received via postMessage:', event.data);
    }
    //Optional: If you opened a dialog, when onChoose the dialog propagates the dialogResponse event action
    if (event.data.action === 'dialogResponse') {
        console.log("Dialog response received via postMessage:", event.data);
    }
  },
  false
);

Magnolia sends the following message to the field iframe:

Property Description

action

  • init: to initialize the field properties and values.

  • error: to notify the field failed the validation (either via the required flag or a validator).

  • change: to notify the field about any changes in the form.

  • restResponse: to notify the field about the response of a called REST client.

  • dialogResponse: to notify the field the selection of a chooser dialog.

correlationId

The ID used by Magnolia to establish communication between the backend and the frontend.

In version 4.0.x and later, using correlationId in your messaging is optional. If you don’t set it, it’s set up automatically for you.

state

The component state.

See the Vaadin section.

Magnolia can receive following messages from the iframe:

Property Description

action

  • changeValue: sent when the value of the form field gets updated.

  • changeFormFieldValue sent when any other value of the form should be updated.

  • changeHeight: sent when the height of the field gets updated by the user in the form.

  • callRestClient: sent when there is a call to a Magnolia REST client.

  • openDialog: sent to open a native Magnolia dialog.

See Helper API.

You can style the iframe directly, for example: window.frameElement.style.position = 'fixed'; window.frameElement.style.top = '0';

correlationId

The ID used by Magnolia to establish communication between the backend and the frontend. Optional from version 4.x.

value

For changeValue, the type of the value is always a string. If you need to store complex objects, stringify it before returning it to Magnolia. For resolving complex object such as JSON array, you need to configure the following reference resolver: info.magnolia.ui.javascript.resolver.NodeListJcrReferenceResolverDefinition. From version 4.0.x it supports any object as it will set the value directly on the form state manager.

For changeFormFieldValue, fieldName indicates the form field that will be changed. The type of the value is always a string (see changeValue)

For changeHeight, send the height in pixels that the new iframe will be.

For callRestClient, restClientName indicates the name of the Magnolia REST client to call, restClientMethodName indicates the name of the Magnolia REST client method to call, and body optional JSON body for the REST client to call.

Helper API

To simplify development, Magnolia injects a helper API into your iframe (window.iframeClient.magnoliaAPI) as a wrapper around the standard postMessage API. You can use this API or the raw postMessage approach. Inspect it in your browser dev tools under window.iframeClient.magnoliaAPI.

  • changeHeight: window.iframeClient.magnoliaAPI.changeHeight(height)

  • changeValue: window.iframeClient.magnoliaAPI.changeValue(value)

  • changeLocation: window.iframeClient.magnoliaAPI.changeLocation(tabTitle, app, subapp, additionalParams)

  • openDialog: window.iframeClient.magnoliaAPI.openDialog(dialogId, workspace, callerId)

  • callRestClient: window.iframeClient.magnoliaAPI.callRestClient(restClientName, restClientMethodName, callerId, body)

  • changeFieldValue: window.iframeClient.magnoliaAPI.changeFieldValue(fieldName, value)

  • 4.x with the helper API

  • 3.x

Example
window.iframeClient.magnoliaAPI.changeValue(input.value);

window.iframeClient.magnoliaAPI.changeHeight(height);

window.iframeClient.magnoliaAPI.changeFieldValue("name", input.value);

window.iframeClient.magnoliaAPI.callRestClient("wonders", "allWonders", "callRestBtn", {});

window.iframeClient.magnoliaAPI.openDialog('pages-app:chooser', 'website', 'callerDOMIdentifier');

// To navigate to a different location, you can send additional attributes as the last parameter
window.iframeClient.magnoliaAPI.changeLocation("title", "pages-app", "browser", undefined);
Example
parent.window.postMessage({
    action: 'changeValue',
    correlationId,
    value: input.value
}, '*');

parent.window.postMessage({
    action: 'changeHeight',
    correlationId,
    value: 50
}, '*');

parent.window.postMessage({
    action: 'changeFormFieldValue',
    correlationId,
    value: 50,
    fieldName: 'other-field-name'
}, '*');

parent.window.postMessage({
  action: "callRestClient",
  correlationId: correlationId,
  restClientName: "ShopProducts",
  restClientMethodName: "list",
  body: ""
}, '*');

parent.window.postMessage( {
  action: "openDialog",
  correlationId,
  dialogId: "ui-framework-core:chooser",
  workspace: "personas",// required for chooser dialogs, optional for standard dialog
  callerId: "unique-identifier",
}, "*",);

// To navigate to a different location, you can send additional attributes as the last parameter
window.iframeClient.magnoliaAPI.changeLocation("title", "pages-app", "browser", undefined);

Standard event messaging system used in 3.x

For openDialog and callRestClient, callerId indicates a unique DOM element ID of the frontend component that posted the command, dialogId indicates a registered dialog, workspace used to specify workspace for chooser dialogs. workspace property is either a JCR workspace or the value rest for REST based data sources, or ecommerce for integrations using the Commerce Integration Framework.

The Commerce Integration Framework must be installed separately in your webapp to use ecommerce based dialogs.

Custom app

Build Magnolia apps with your own frontend: define a JavaScript app, load it from a module or external URL, and communicate with Magnolia using the message-based API.

Configuration

Add a JavaScript app in your app definition as follows:

icon: icon-categories
class: info.magnolia.ui.contentapp.configuration.ContentAppDescriptor
appClass: info.magnolia.ui.framework.app.BaseApp
label: Custom app
subApps:
  firstApp: (1)
    class: info.magnolia.ui.javascript.app.JavascriptSubAppDescriptor (2)
    label: 1st tab
    fieldScript: /ui-framework-javascript-dev-samples/webresources/debugApp.html (3)
    parameters: (4)
      welcomeMessage: Browser
  secondApp: (5)
    class: info.magnolia.ui.javascript.app.JavascriptSubAppDescriptor
    label: 2nd tab
    fieldScript: /ui-framework-javascript-dev-samples/webresources/debugEditApp.html
    closable: false (6)
    parameters:
      welcomeMessage: Editor
1 First subapp listed in subApps:
  • Is shown when opening the app (for example, via AdminCentral tile).

  • Has an unclosable tab.

2 Must be info.magnolia.ui.javascript.app.JavascriptSubAppDescriptor.
3 Points to a valid HTML file located in:
4 Pass custom parameters to the JavaScript field.
5 All but the first listed subapp under subApps, by default:
  • Aren’t shown when opening the app.

  • Have closable tabs.

6 Set to false to modify the default behavior described in point 5.

Message format

Communication between Magnolia and your iframe is done with the message event.

An iframe can listen for dispatched messages by executing the following JavaScript:

window.addEventListener(
  'message',
  function (event) {
    if (event.data.action === 'init') {
      console.log(event.data.correlationId);
      console.log(event.data.state);
    }
  },
  false
);

Magnolia sends the following message to the app iframe:

Property Description

action

  • init: to initialize the app properties and values.

correlationId

The ID used by Magnolia to establish communication between the backend and the frontend.

It must be passed in all messages sent to the backend.

state

The component state

See the Vaadin section.

Magnolia can receive following messages from the iframe:

Property Description

action

  • changeLocation: Change the Vaadin location (used to open subapps).

correlationId

The id used by Magnolia to establish communication between the backend and the frontend.

app

The name of the app to forward to.

subapp

The name of the subapp to forward to.

tabTitle

The target subapp tab caption.

urlParameter

The concatenated parameters to be passed to the target subapp with state object.

Example
parent.window.postMessage({
    action: 'changeLocation',
    correlationId,
    app: 'custom-app',
    subapp: 'secondApp',
    tabTitle: 'My tab title',
    urlParameter: 'my-object-id'
}, '*');

Custom JavaScript dialogs

Custom JavaScript dialogs let you integrate any frontend as a Magnolia dialog. Define your own dialog script and implement the message-based communication described below.

Configuration

Dialog definition

Add a JavaScript dialog as follows:

fieldScript: /<your-lm>/webresources/js-dialog/app.html (1)
parameters:
  your-test: parameter
1 Points to a valid HTML file located in:
To ensure that the dialog matches Magnolia’s new UI, you can use an implementation that uses Magnolia’s new Design System.

Open dialog action

Add an open dialog action as follows:

openJSDialog:
  label: Open JS Dialog
  icon: icon-file-image
  $type: openJavascriptDialogAction (1)
  javascriptDialogId: <your-lm>:<your-dialog> (2)
1 $type must be set to openJavascriptDialogAction.
2 Points to the ID of the defined dialog.

Message format

Communication between Magnolia and your iframe is done with the message event.

An iframe can listen for dispatched messages by executing the following JavaScript:

window.addEventListener(
  'message',
  function (event) {
    if (event.data.action === 'init') {
      console.log(event.data.correlationId);
      console.log(event.data.state);
    }
  },
  false
);

Magnolia sends the following message to the field iframe:

Property Description

action

  • init: to initialize the field properties and values.

  • error: to notify the field failed the validation (either via the required flag or a validator).

  • change: to notify the field about any changes in dialog

  • restResponse: to notify the field about the response of a called REST client

  • dialogResponse: to notify the field the selection of a chooser dialog

correlationId

The ID used by Magnolia to establish communication between the backend and the frontend.

It must be passed in all messages sent to the backend.

state

The component state

See the Vaadin section.

Magnolia can receive following messages from the iframe:

Property Description

action

  • callRestClient: sent when there is a call to a Magnolia REST client.

  • openDialog: sent to open a native Magnolia dialog.

  • closeDialog: sent to close the JavaScript dialog.

All iframe styles can be also changed directly on the DOM element e.g.:
window.frameElement.style.position = 'fixed';
window.frameElement.style.top = '0';

correlationId

The ID used by Magnolia to establish communication between the backend and the frontend.

For openDialog:

  • callerId indicates a unique ID of the frontend component that posted the command.

  • dialogId indicates a registered dialog.

  • workspace is used to specify workspace for chooser dialogs.

    The workspace property is either a JCR workspace or the value rest for REST based datasources or ecommerce for integrations using Commerce Integration Framework.

The Commerce Integration Framework must be installed separately in your webapp to use ecommerce based dialogs.
Example
parent.window.postMessage({
  action: "callRestClient",
  correlationId: correlationId,
  restClientName: "ShopProducts",
  restClientMethodName: "list",
  body: ""
}, '*');

parent.window.postMessage( {
  action: "openDialog",
  correlationId,
  dialogId: "ui-framework-core:chooser",
  workspace: "personas",// required for chooser dialogs, optional for standard dialog
  callerId: "unique-identifier",
}, "*",);

parent.window.postMessage( {
  action: "closeDialog",
  correlationId,
}, "*",);

Developer sample

A developer sample can be found here.

Vaadin

Vaadin shares a component state with the JavaScript field, here is its format:

Property Description

contextPath

The current Magnolia instance context path.

Example: /author

fieldScriptUrl

The field script URL defined in the yaml definition.

Example:

/my-light-module/webresources/colorField/index.html

parameters

The parameters defined in the yaml definition.

currentUser

The current user.

locale

The current locale.

Only in Custom form fields

currentNode

The current node.

This is null in case of a node creation.


Click to see example

{
    "id":"8346c262-689b-4622-98ac-a56e15c59163",
    "name":"tests",
    "path":"/tests",
    "properties":{
        "mgnl:created":"2022-01-21T11:42:04.143+01:00",
        "mgnl:template":"my-light-module:pages/home",
        "jcr:created":"2022-01-21T11:42:04.143+01:00",
        "mgnl:lastModifiedBy":"superuser",
        "jcr:createdBy":"admin",
        "mgnl:createdBy":"superuser",
        "jcr:uuid":"8346c262-689b-4622-98ac-a56e15c59163",
        "jcr:primaryType":"mgnl:page",
        "colorField":"#ff0040",
        "mgnl:lastModified":"2022-01-21T11:42:04.143+01:00"
    },
    "type":"mgnl:page"
}

defaultValue

The default value defined in the yaml definition.

formFields

The values of the other fields of the current form.

height

The height defined in the yaml definition, or the updated value sent in case the field gets enlarged from the UI.

Example: 50

value

The current value of the form property.

readOnly

Whether the field is read only or not.

Only in Custom app

urlParameter

Used to communicate parameters between two javascript subapps.

Samples

Check out some samples here.

Also you can check a complete example with all the features on the following html here. To test this example, you can use this form template after setting the example REST endpoint:

Example
javascriptField:
    label: Javascript field example
    $type: javascriptField
    fieldScript: http://localhost:3000/jsComplexTextField.html
    height: 20

Ecommerce

If you want to use Ecommerce dialogs, you must install the Commerce Integration Framework in your webapp and activate the product picker feature:

  1. Make sure the E-commerce module is installed and set up properly.

  2. Go to your /<your-maven-module>/src/main/resources/META-INF/magnolia/<your-maven-module>.xml

  3. Ensure you have the following in the file.

    <your-maven-module>/src/main/resources/META-INF/magnolia/<your-maven-module>.xml
      <components>
        <id>main</id>
        <component>
          <type>info.magnolia.ui.javascript.dialogs.impl.EcommerceDialogResultMapper</type>
          <implementation>info.magnolia.ui.javascript.dialogs.impl.EcommerceDialogResultMapper</implementation> (1)
          <scope>singleton</scope>
        </component>
      </components>
    1 Ensure you enable the EcommerceDialogResultMapper component in scope main.

Changelog

Version Notes

4.0.0

Compatible with Magnolia 6.4 and the New UI Forms.

Feedback

DX Core

×

Location

This widget lets you know where you are on the docs site.

You are currently perusing through the JavaScript UI module docs.

Main doc sections

DX Core Headless PaaS Legacy Cloud Incubator modules