JavaScript UI module
Developer productivity Unbundled: Extension Version 4.0.0 Magnolia 6.4 compatible
Issues |
|
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:
In 4.x, if this class is still present, the form falls back to the legacy Vaadin forms. We recommend removing the This does not apply to JavaScript dialogs, where |
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 | ||
---|---|---|---|
|
|
||
|
The ID used by Magnolia to establish communication between the backend and the frontend.
|
||
|
The component state.
|
Magnolia can receive following messages from the iframe:
Property | Description | ||
---|---|---|---|
|
See Helper API.
|
||
|
The ID used by Magnolia to establish communication between the backend and the frontend. Optional from version 4.x. |
||
|
For For For For |
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)
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);
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 :
|
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:
|
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 | ||
---|---|---|---|
|
|
||
|
The ID used by Magnolia to establish communication between the backend and the frontend.
|
||
|
The component state
|
Magnolia can receive following messages from the iframe
:
Property | Description |
---|---|
|
|
|
The id used by Magnolia to establish communication between the backend and the frontend. |
|
The name of the app to forward to. |
|
The name of the subapp to forward to. |
|
The target subapp tab caption. |
|
The concatenated parameters to be passed to the target subapp with |
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. |
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 | ||
---|---|---|---|
|
|
||
|
The ID used by Magnolia to establish communication between the backend and the frontend.
|
||
|
The component state
|
Magnolia can receive following messages from the iframe:
Property | Description | ||
---|---|---|---|
|
|
||
|
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 valuerest
for REST based datasources orecommerce
for integrations using Commerce Integration Framework.
The Commerce Integration Framework must be installed separately in your webapp to use ecommerce based dialogs.
|
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 | ||
---|---|---|---|
|
The current Magnolia instance context path. Example:
|
||
|
The field script URL defined in the Example:
|
||
|
The parameters defined in the |
||
|
The current user. |
||
|
The current locale. |
||
|
|||
|
The current node.
Click to see example
|
||
|
The default value defined in the |
||
|
The values of the other fields of the current form. |
||
|
The height defined in the Example:
|
||
|
The current value of the form property. |
||
|
Whether the field is read only or not. |
||
|
|||
|
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:
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:
-
Make sure the E-commerce module is installed and set up properly.
-
Go to your
/<your-maven-module>/src/main/resources/META-INF/magnolia/<your-maven-module>.xml
-
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 scopemain
.
Changelog
Version | Notes |
---|---|
|
Compatible with Magnolia 6.4 and the New UI Forms. |