This directory contains the source of a JavaScript loader library used to integrate the chatbot UI component in an HTML page. The chatbot UI component is the reusable widget that encapsulates the Lex Web UI functionality (see its README for details about the component).
This loader library provides a runtime script used to integrate the chatbot UI component with a web page. The loader library assists with the process of adding the component to the DOM of a web page and loading its configuration and dependencies. It can be used to integrate the chatbot UI component into an existing website as a full page or embedded in an iframe.
You can test the loader library on a development host using the sample HTML pages under the website directory. These sample pages can be served using a local HTTP server provided by this project ( requires a recent version of Node.js).
As a minimum, you need to need a valid Cognito Identity Pool and Lex Bot to use the chatbot UI component. The Cognito Identity Pool needs to have the right permissions to access the Lex bot (See the Credential Management section of the chatbot UI component README).
NOTE: If you deployed the sample website using CloudFormation as described in the Deploying section of the main README, you can put the Cognito Identity Pool Id and Lex Bot name created by CloudFormation in the local config. If you already have a Lex bot and you just want to create a Cognito Identity Pool suitable for testing, you can use the cognito.yaml CloudFormation template.
The samples pages can obtain its configuration from the file config/lex-web-ui-loader-config.json. Set the Cognito Pool Id and Lex Bot name in the file:
// file: /src/config/lex-web-ui-loader-config.json
...
"cognito": {
"poolId": "<my cognito pool id>"
...
"lex": {
"botName": "<my bot name>"
...
Before you run the test server, you have to install its dependencies by running the following command from the root directory of the repo:
npm install
Once you have the right config, you can run the test HTTP server on:
http://localhost:8000
by executing the following command under the
root directory of this repo:
npm start
The sample pages available from the test server include index.html and parent.html which demonstrate how to load the chatbot UI component as a full page or as an iframe respectively.
The sample pages in this repository, use a JavaScript library to load the chatbot UI component. You can use this library on your website to add the chatbot UI as a full page or as an iframe. The source of this library is found under the lex-web-ui-loader directory.
In essence, the loader performs the following functions:
- Loads the chatbot UI configuration. The UI's look-and-feel (e.g. colors, titles) and resources (e.g. Lex bot name, Cognito Pool Id) can be configured from various sources (e.g. JSON files, events or programmatically). The loader can be used to control how the config is loaded. See: Chatbot UI Configuration Loading
- Loads the chatbot UI component JavaScript and CSS files. The chatbot UI component depends on various script and CSS files including third-party libraries (e.g. AWS SDK, Vue.js). The loader takes care of automatically adding these dependencies as well. See: Dependencies Loading
- Creates an API between the parent and iframe when running the chatbot UI embedded in a page. See: Iframe API
- Instantiates the chatbot UI component and adds it to the DOM
In its most simple form, you can copy the files in the dist
directory
to your web server and include the loader script in your HTML page:
<!-- loader library script -->
<script src="./lex-web-ui-loader.js"></script>
<!--
at this point the library global object `ChatBotUiLoader` is present
and the library is ready to be used to load the chatbot UI
-->
NOTE: This project provides automated ways to deploy the files and configuration to an S3 bucket so that you do not have manually copy or change files. See: Deploying.
The lex-web-ui-loader.js
script in the dist
directory is a pre-built
version of the loader library described in this page. The library is
built using webpack (see dependencies and
build setup). When you add the library
script to your page, it creates a global object that is used to load
the chatbot UI component.
The loader script creates a global object named ChatBotUiLoader
. In
turn, this object contains two fields that are constructors used to
create a loader object depending on the use case:
FullPageLoader
. Loads the chatbot UI as a full page. See the Full Page section for detailsIframeLoader
. Loads the chatbot UI as an iframe in a parent page. See the Iframe Embedding for details
An instance of the loaders can be used to load the chatbot UI by calling
its load
function. For example:
// lex-web-ui-loader.js has already been included
// load as a full page using default values
new ChatBotUiLoader.FullPageLoader().load();
To use the loader library, you 1) instantiate a loader object and 2)
call the load
function of this object. See the next two sections for
quick examples of these steps.
You first need to instantiate a loader object with one of constructors depending on the use case (full page or iframe). Please note that instantiating the loader does not load the chatbot UI component. It creates an object that is used to load the component.
The loader constructors takes an options parameter used to control how the chatbot UI component will load. You can see the available options and defaults in the Loader Constructor Options section.
For example:
var loaderOpts = {
shouldLoadConfigFromJsonFile: true,
configUrl: 'https://mysite.example.com/configs/lex-web-ui-loader-config.json',
shouldLoadConfigFromEvent: true,
configEventTimeoutInMs: 3000,
};
// Instantiate the loader by optionally passing the loader options to
// control its behavior
var loader = new ChatBotUiLoader.FullPageLoader(loaderOpts);
Once you have an instance of the loader, you call the
load
function to trigger the load process as described in the
Functionality section above. The load function takes a
chatbot UI component configuration object as an argument. This argument
is merged with the configuration passed from other sources (e.g. JSON
file, event) and overrides their values. For details see: Chatbot UI
Configuration Loading section.
For example:
// continuation from script above
// override the toolbar title
var chatbotUiConfig = {
ui: {
toolbarTitle: 'My Bot'
}
};
// pass chatbot UI config to load function
loader.load(chatbotUiConfig)
// returns a promise that is resolved once the chatbot UI is loaded
.then(function () {
console.log('chatbot UI loaded');
})
.catch(function (error) {
console.error('chatbot UI failed to load', error);
});
The FullPageLoader
and IframeLoader
constructors take an option
parameter with the following fields:
var options = {
// base URL to be prepended to relative URLs of dependencies
// if left empty, a relative path will still be used
baseUrl: '/',
// time to wait for config event
configEventTimeoutInMs: 10000,
// URL to download config JSON file
// uses baseUrl if set as a relative URL (not starting with http)
configUrl: './lex-web-ui-loader-config.json',
// controls whether the local config should be ignored when running
// embedded (e.g. iframe) in which case the parent page will pass the config
// Only the parentOrigin config field is kept when set to true
shouldIgnoreConfigWhenEmbedded: true,
// controls whether the config should be obtained using events
shouldLoadConfigFromEvent: false,
// controls whether the config should be downloaded from `configUrl`
shouldLoadConfigFromJsonFile: true,
// Controls if it should load minimized production dependecies
// defaults to true for production builds and false in development
shouldLoadMinDeps: false,
// DOM element ID where the chatbot UI will be mounted
// defaults to 'lex-web-ui-fullpage' in full page mode
// and 'lex-web-ui-iframe' in iframe mode
elementId: 'lex-web-ui-fullpage',
// div container class to insert iframe
containerClass: 'lex-web-ui-iframe',
// iframe source path. this is appended to the iframeOrigin
// must use the LexWebUiEmbed=true query string to enable embedded mode
iframeSrcPath: '/index.html#/?lexWebUiEmbed=true',
};
The chatbot UI component (the actual widget rendered in a web page) supports a number of configuration and customization options. The loader library in this directory facilitates passing this config to the component.
The config object passed by the loader to the component has the
same structure as the component's base config. For a list of
fields and default values see configDefault
object in the:
/lex-web-ui/src/config/index.js file.
The loader library can use various sources to compose the chatbot UI component config. The configuration values from these sources are merged in order of precedence into a single object that is passed to the component when it gets created. Here is a list of the configuration sources in order of precedence (lower overrides higher):
See the sections below for a description of each.
The config can be passed as a parameter to the load
function of the
loader instance. This is used when you want to have a programmatic way
to directly override the chatbot UI component config through the loader
instance just before the component is instantiated. See the example in
the Call the Load Function section for an
illustration of how to pass this parameter.
The loader library can use events to pass a config object to the
chatbot UI component. This is useful when you want an asynchronous and
decoupled way to pass a the config. This mechanism is disabled by
default and can be enabled by setting the shouldLoadConfigFromEvent
loader constructor option to true
.
At creation time, the chatbot UI
component emits the receivelexconfig
custom
event
which sends a signal to the loader that the component is ready to receive
a configuration object. When shouldLoadConfigFromEvent
is enabled,
the loader waits for a 10 seconds timeout (default) to receive this
event. The timeout can be controlled by the configEventTimeOutInMs
field in the constructor options.
Once you have received the receivelexconfig
event, you send the
configuration by emitting a loadlexconfig
event with the config object
contained in the detail.config
field.
For example, the code below dynamically passes the browser user agent, to the Lex bot as a session attribute which will be merged with the chatbot UI config:
document.addEventListener('receivelexconfig', onReceiveLexConfig, false);
function onReceiveLexConfig() {
document.removeEventListener('receivelexconfig', onReceiveLexConfig, false);
var config = {
lex: {
sessionAttributes: {
userAgent: navigator.userAgent,
},
},
};
var event = new CustomEvent('loadlexconfig', { detail: { config: config } });
document.dispatchEvent(event);
}
The configuration object can be sourced from a JSON file. By default,
the loader fetches a file named lex-web-ui-loader-config.json
in the
same directory where the library resides and merges its values into the
config object.
See the
lex-web-ui-loader-config.json
file for a sample of its structure. The URL of the JSON file
is controlled by the configUrl
loader constructor
option. Please
note that if the URL points to another
Origin,
the web server needs to correctly set
CORS
Access Controls
Headers.
The loader library dynamically adds the chatbot UI component files and its runtime dependencies (e.g. Vue.js, Vue, Vuetify, AWS SDK) to the DOM. By default, the chatbot UI runtime dependencies are loaded from third-party CDNs. (NOTE: You may want to host these third-party dependencies on your own site for production.)
The chatbot UI component is provided as pre-built JavaScript
and CSS files under the /dist directory (lex-web-ui.*
files). The loader uses those files to load the chatbot UI. At
runtime, the chatbot UI depends on third party libraries (see the
dependencies section of the
component README file for details).
The URLs and load order of these dependencies are controlled
by the dependencies
option of the loader constructor. You
can see the structure and defaults of this option in the
dependencies.js file.
By default, the chatbot UI component files are added from a relative URL.
If you want to point the dependencies to another URL, you can use the
baseUrl
option of the loader constructor. For example:
var loaderOpts = {
baseUrl: 'https://my-cdn.example.org/'
}
var new iframeLoader = new ChatBotUiLoader.IframeLoader(loaderOpts);
// when you call iframeLoader.load(), it will obtain the chatbot UI
// component files relative from baseUrl
The loader library can render the chatbot UI component to take the full
size of a page. This can be used when you want to pop-up the chatbot
on a dedicated page. The index.html file under the
website
directory shows how to load the chatbot UI as a full page.
Here is an HTML snippet showing how to render the chatbot UI as a full page:
<!--
Add the loader library to the page.
-->
<script src="./lex-web-ui-loader.js"></script>
<!--
Loader script creates a global variable named ChatbotUiLoader.
-->
<script>
// ChatBotUiLoader contains the FullPageLoader constructor
var lexWebUiLoader = new ChatBotUiLoader.FullPageLoader();
// An instance of FullPageLoader, by default, loads the chatbot UI
// config from ./chatbot-ui-loader-config.json
// These config files point to the Lex bot, Cognito Pool and other
// chatbot UI configurable features.
// If those config files are present whith the appropriate configuration,
// the load function will load the chatbot UI component.
lexWebUiLoader.load();
</script>
For greater configuration control, you can pass options to the loader and configuration to the chatbot UI component:
<!-- LexWebUi loader -->
<script src="./lex-web-ui-loader.js"></script>
<script>
// loader options control how the loader obtains the config and
// dependencies. It can be optionally passed as an argument to the
// FullPageLoader constructor to override the defaults
var loaderOptions = {
configUrl: './chatbot-ui-loader-config-2.json'
};
// instantiate the loader
var lexWebUiLoader = new ChatBotUiLoader.FullPageLoader(loaderOptions);
// this config is used to pass run-time configuration to the chatbot UI
// as an argument to the load() function. The config passed through
// load() is merged with the config from other sources and overrides
// their values
var chatbotUiconfig = {
cognito: {
poolId: 'us-east-1:deadbeef-fade-babe-cafe-0123456789ab'
},
lex: {
initialText: 'How can I help you?',
botName: 'helpBot'
},
ui: {
toolbarTitle: 'Help Bot',
toolbarLogo: ''
},
};
// You can pass the config as a parameter.
// The loader can also get its config dynamically
// from a JSON file
lexWebUiLoader.load(chatbotUiConfig)
// returns a promise that is resolved once the chatbot UI is loaded
.then(function () {
console.log('chatbot UI loaded');
})
.catch(function (error) {
console.error('chatbot UI failed to load', error);
});
</script>
Several requests were made to enable an API when using the Full Page mode to directly utilize the VUE component. There are two approaches to satisfying this need. The first would be to access the component using the Vue Global registration of the component. The methods and properties available for the component then become available to the client including the LexClient. The VueComponent would be directly accessed to post a text message to Lex. If not using VUE this approach becomes more difficult.
The second approach as an alternative is to use a new API available in preview mode. This API is available from the FullPageComponentLoader (fullpage-component-loader.js).
The API supports two operations
- ping()
- postText(message)
Differing from the Iframe API, both operations complete asynchronously returning immediately. Ping will be responded to by the VUE component with a "pong" message.
postText(message) will invoke the Lex API and call postText against Lex. No return or confirmation is supplied.
var Loader = ChatBotUiLoader.FullPageLoader;
var loaderOpts = {
};
var loader = new Loader(loaderOpts);
var chatbotUiConfig = {
lex: {
sessionAttributes: {
userAgent: navigator.userAgent
}
}
};
loader.load(chatbotUiConfig);
...
...
...
**loader.compLoader.ping();**
**loader.compLoader.postText("Hello");**
The use of loader.compLoader.ping() or loader.compLoader.postText("message") are examples of invoking the API.
Please provide feedback on the use of this API.
The loader library can add the chatbot UI component to an existing page as an iframe. This works by dynamically creating an iframe that wraps the full page setup described above. This is used when you want to embed the chatbot UI as a contained widget in an existing page.
This approach also provides an API that allows interaction
between the hosting parent page and the chatbot UI component. The
parent.html file in the website
directory is
a sample implementation of embedding the chatbot UI in an iframe.
In addition to the chatbot UI component config described in the ChatBot
UI Configuration Loading section,
the loader library supports an extra config field named iframe
that
provides control of the iframe loading process.
{
// same fields as the lex-web-ui component
lex: {...},
...
ui: {
// controls which origin can communicate and load the chatbot UI
parentOrigin: 'https://parentpage.example.com',
},
...
// add an iframe field used when embedding in an iframe
iframe: {
// origin of the iframe containing the chatbot UI
iframeOrigin: 'https://iframe.example.com',
// Path and url query string used to point where
// where the page chatbot UI page is located and to indicate that
// the component is loading as an iframe.
// This is appended to iframeOrigin
iframeSrcPath: '/index.html#/?lexWebUiEmbed=true',
// indicates if the chatbot UI in the iframe should load minimized
shouldLoadIframeMinimized: true,
}
}
As you may have noticed in the example above, the iframe can be loaded
using a cross-origin setup where the parent page and iframe are hosted
on different
Origins.
For this setup to work, you need to configure the correct origins in
the iframeOrigin
field under the iframe
key and the parentOrigin
under the ui
key.
This configuration is used to control which parent sites can communicate with the iframe and vice-versa. When deploying with the CloudFormation templates from this project, the origin fields are automatically set in the loader JSON config file.
SECURITY NOTE: You should set the origin fields to be as specific as possible to prevent unauthorized hosts from interacting with your page. See details in the data passing section of the API.
The snippet below is a cross-origin example showing how to load the iframe to an existing page. In this example, you host the loader and chatbot UI files in a separate web server (this can be an S3 bucket or a CloudFront distribution). This web server must hold all the JavaScript and CSS files in the /dist directory.
NOTE: This project provides automated ways to deploy the files and configuration to an S3 bucket so that you do not have manually copy them. See: Deploying.
<!--
Add a script tag pointing to the loader library towards the bottom
of the html BODY section
The library and its dependencies can be hosted on another site,
S3 bucket or a CDN (e.g. CloudFront)
This script creates a global variable named ChatbotUiLoader which
provides the loader library functionality
-->
<script src="https://iframe-host.example.org/lex-web-ui-loader.js">
</script>
<!--
After the loader script tag has been included, you can use it to
load the chatbot UI in an iframe
-->
<script>
// Point the baseUrl to download the dependencies from the site hosting
// the loader and the chatbot UI files
// This url should host the JavaScript and CSS files in the dist directory
var loaderOpts = {
baseUrl: 'https://iframe-host.example.org/'
}
// ChatBotUiLoader contains the IframeLoader constructor
// Instantiate the loader and pass the loader options
var lexWebUiLoader = new ChatBotUiLoader.IframeLoader(loaderOpts);
// You can override the chatbot UI config by passing a parameter
// if the iframe page is hosted on a different site,
// you should configure the parentOrigin and iframeOrigin
var chatbotUiconfig = {
ui: {
// origin of the parent site where you are including the chatbot UI
parentOrigin: 'https://mysite.example.org'
},
iframe: {
// origin hosting the HTML file that will be embedded in the iframe
iframeOrigin: 'https://iframe-host.example.org'
}
};
// load the iframe
lexWebUiLoader.load(chatbotUiconfig)
.catch(function (error) {
console.error('chatbot UI failed to load', error);
});
</script>
The loader library provides an API that can be used to dispatch actions from the parent page to the chatbot UI iframe (e.g. send text content to the bot) or to receive requests from the chatbot UI iframe (e.g. get credentials).
This API can be invoked by using events and/or functions in an instance of
IframeLoader
. It enables use cases such as:
- Passing credentials from the parent hosting site to the chatbot iframe
- Programmatically sending a text message to be sent to the bot by the chatbot UI
- Passing events (e.g. windows minimize) from the chat window to the parent window
The API can be used with two mechanisms:
- Iframe Loader API functions. The
loader object created with
IframeLoader
has a set of functions that can be used to call actions in the chatbot UI component - Iframe Loader Event Based API. The loader library transparently relays events to and from the chatbot UI component. You can use these events to receive updates from the iframe or to emit action events to the chatbot UI
Instances of IframeLoader
have a field named api
which contains
functions used invoke actions in the chatbot UI component. These functions
return a promise that is resolved when the function is successfully
completed or rejected if there was an issue.
This is a list of the available functions:
postText(textMessage,messageType)
. Sends an utterance to the bot from the UI. Lex Web Ui supports input from two elements. Input from the user or input from a ResponseCard button. messageType is an optional string parameter that specifies how postText should treat the input. messageType can be set to the value 'human' or 'button'. Messages with the 'human' message type are always displayed in the list of messages in the UI. Messages with the 'button' message type are optionally displayed to the user depending on the configuration value of 'ui.hideButtonMessageBubble'. If 'ui.hideButtonMessageBubble' is set to true, messages with the messageType of 'button' are hidden. If 'ui.hideButtonMessageBubble' is set to false, messages with the messageType of 'button' are displayed in the message list. The default for 'ui.hideButtonMessageBubble' is false where all postText messages and ResponseCard button clicks will be displayed as input from the user. The api has two properties that can be used to specify messageType when calling postText. These are api.MESSAGE_TYPE_HUMAN and api.MESSAGE_TYPE_BUTTON.toggleMinimizeUi()
. Toggles the chatbot UI between being minimized and maximizedping()
. Tests communication with the chatbot UI componentstartNewSession()
. Builds a new Lex Session and sets the Bot to elicit an intentdeleteSession()
. Calls the Lex DeleteSession api to terminate a Lex sessionsetSessionAttribute(key, value)
. Allows the parent page to set session attributes by specifying a 'key' and a 'value'. Both are strings. 'key' should be a top level key. 'value' must be a string and not an object. Should you want to store an entire object, stringify this object.
You can use it like this:
var lexWebUiLoader = new ChatBotUiLoader.IframeLoader();
// you should wait until the chatbot UI component is loaded
// before using the API. The load function is a promise that
// resolves once the component is fully loaded
lexWebUiLoader.load()
.then(function () {
// test communication channel
return lexWebUiLoader.api.ping();
})
.then(function () {
// toggles chatbot UI between minimize and maximized
return lexWebUiLoader.api.toggleMinimizeUi()
})
.then(function () {
// send an utterance to the bot
return lexWebUiLoader.api.postText('Buy flowers', lexWebUiLoader.api.MESSAGE_TYPE_HUMAN)
})
.catch(function (error) {
console.error('oops... ', error);
});
The lex-web-ui-loader.js
loader script relays messages to
and from the chatbot UI component using events. It serves as a
proxy to the low level postMessage mechanism discussed in the
Iframe API Details section. It works by using custom
events
and event
listeners
in the parent page that pass data to and from the low level message
interface.
The chatbot UI iframe sends Lex bot state update events to the parent
site via postMessage. This can be used to have the parent react to the
dialog state of the bot. An instance of IframeLoader
creates an event
handler to receive these messages and relays them back to the parent
by emitting updatelexstate
events. Thes event objects contain the Lex
state variables in the details.state
field.
For example, the following snippet grabs the Lex response variables from the events:
// the bot loader script receives the state from the iframe via postMessage
// and relays it to the parent by dispatching 'updatelexstate' events
document.addEventListener('updatelexstate', onUpdateLexSate, false);
function onUpdateLexState() {
var slots = evt.detail.state.slots;
var dialogState = evt.detail.state.dialogState;
var intentName = evt.detail.state.intentName;
var sessionAttributes = evt.detail.state.sessionAttributes;
var responseCard = evt.detail.state.responseCard;
}
The lex-web-ui-loader.js
script creates an event handler to receive
messages from the parent site that are relayed down to the iframe using
postMessage. This handler listens on lexWebUiMessage
events. The event
object should contain the message in the details.message
field. The
message should contain an event
field with the event name. For example,
to dynamically minimize the chatbot UI, you can send a toggleMinimizeUi
message from the parent like this:
var event = new CustomEvent(
'lexWebUiMessage',
// chatbot ui has a handler for the toggleMinimizeUi event
{ detail: {message: {event: 'toggleMinimizeUi'} } }
);
// this event is relayed to the iframe using postMessage
document.dispatchEvent(event);
The lex-web-ui-loader.js
script emits the lexWebUiReady
event when it
has finished loading the chatbot UI. This event signals the parent that
the chatbot UI component it is ready to interact dynamically. You should
wait for this event before you start sending messages to the iframe.
For example, the following JQuery snippet sends a ping message once the chatbot UI is ready:
$(document).on('lexWebUiReady', function onUpdateLexState(evt) {
var event = new CustomEvent(
'lexWebUiMessage',
{ detail: {message: {event: 'ping'} } }
);
document.dispatchEvent(event);
});
This section describes the chatbot UI component low level API used when
it runs in embedded mode (iframe). The lex-web-ui-loader.js
loader
script implements this low level API and provides a compatibility layer
using functions and event (See Iframe API). You may want
to use that compatibility layer instead of the examples here which are
meant to be an illustration of how it works under the hood. This section
is meant to provide details to someone trying to re-implement the loader
API functionality.
The chatbot UI component activates an API when the page loading it has
the lexWebUiEmbed
URL query parameter set to true
. This API can be
used to pass data to and from a hosting parent page. This is done using
the JavaScript
postMessage
call. The messages use the
MessageChannel
API to asynchronously send responses to the events. The API can pass data
between the parent hosting site and the chatbot UI using a dedicated
message channel in each event. It also uses the message channel to
respond an event as resolved or rejected to pose as a
Promise.
SECURITY NOTE: Passing messages with postMessage can work in a
cross-origin setup where the iframe is hosted in a domain/origin different
from the parent site. The chatbot UI component (lex-web-ui.js
) and the
loader (lex-web-ui-loader.js
) scripts validate the event Origin against
configured values as a security access control measure. This avoids
unauthorized sites posting arbitrary messages to your site or posing as
the chatbot UI. Make sure to use origins as specific as possible.
The chatbot UI component receives events from postMessage calls which are
used as an API for functionality in the chatbot UI that can be triggered
from the parent. The API includes a list of event types that are passed
to handlers in the chatbot UI. The event type is included in the event
field of the message object sent to the chatbot UI via postMessage. The
responses to these events are sent back in the associated message channel.
These responses include a field named event
that is set to the string
resolve
when successful and to reject
there was a failure.
The chatbot UI component implements the following event types:
- ping. Logs a pong response in the console. Used for testing
- toggleMinimizeUi. Toggles the iframe from being minimized
to maximized and vice-versa. It allows the chatbot UI to be
minimized/maximized programmatically. This works in conjunction with
the parent page where the
lex-web-ui-loader.js
script minimizes the iframe by resizing it using CSS - parentReady. Signals the chatbot UI that the parent has successfully loaded the chatbot UI and is ready to interact with it
- postText. Injects a text message in the chatbot UI. This
allows to programmatically issue utterances to the bot from the parent
page. The message itself is in the
message
field
For example, you can use the following code to programmatically minimize the chatbot UI by sending the toggleMinimizeUi event type:
var iframeContainerClassSelector = '.lex-web-ui-iframe';
var iframeElement = document.querySelector(
iframeContainerClassSelector + ' iframe'
);
var messageChannel = new MessageChannel();
// event type in event field
var message = { event: 'toggleMinimizeUi' };
// origin is a security access control and should be explicitly set
// as specific as possible
var iframeOrigin = 'https://chatbotiframe.example.com';
function sendMessageToIframe(evt) {
messageChannel.port1.close();
messageChannel.port2.close();
// successful message responses include the event field set to 'resolve'
if (evt.data.event === 'resolve') {
console.log('iframe successfully handled our message', evt.data);
} else {
console.error('iframe failed to handle our message', evt.data);
}
};
messageChannel.port1.onmessage = sendMessageToIframe;
iframeElement.contentWindow.postMessage(message,
iframeOrigin, [messageChannel.port2]);
The chatbot UI sends message
events to the parent page using postMessage
calls. The parent page should create an event listener in its window
object to receive these messages. The event object sent to the message
handler includes its type (or API call) in data.event
field.
The messages contain a message channel that can be used to respond to
the chatbot UI with data. The responses should include an object with
a field named event
with the string value of resolve
to indicate
that the parent page was successful in handling the event. Otherwise,
set the field value to reject
.
The chatbot UI supports the following messages:
- ready. Signals to the parent that the chatbot UI component was successfully initialized
- getCredentials. Requests AWS credentials from the parent to be used for AWS API calls to Lex and Polly. The response to this call should contain an AWS Credentials object (which could be obtained with Cognito)
- initIframeConfig. Requests the initial chatbot UI configuration. This is used to pass a dynamic configuration object to the iframe. The response to this call should have a configuration object. See the Config Object section for details
- toggleMinimizeUi. Requests the parent to minimize the ChatBot UI iframe. This is sent when the ChatBot UI minimize/maximize button is pressed to signal the parent that it should resize the iframe accordingly
- updateLexState. Sends Lex Bot state updates to the parent containing response fields from the Lex Runtime Service calls
Here's an example of how to handle API calls from the chatbot UI:
function onMessageFromChatBot(evt) {
// origin is a security access control and should be explicitly set
// as specific as possible
var iframeOrigin = 'https://chatbotiframe.example.com';
// security check
if (evt.origin !== iframeOrigin) {
console.warn('postMessage from invalid origin', evt.origin);
return;
}
if (!evt.ports) {
console.error('postMessage not sent over MessageChannel', evt);
return;
}
switch (evt.data.event) {
case 'updateLexState':
onUpdateLexState(evt);
break;
// other handlers go here
default:
console.error('unknown message in event', evt);
break;
}
}
function onUpdateLexState(evt) {
// evt.data will contain the Lex state
console.log('lex state update:', evt.data);
// send resolve ressponse to the chatbot ui
evt.ports[0].postMessage({ event: 'resolve', type: evt.data.event });
}
function onGetCredentials(evt) {
// assumes that credential is a valid AWS SDK Credential object
return credential.getPromise()
.then(function resolveGetCredentials(creds) {
evt.ports[0].postMessage({
event: 'resolve',
type: evt.data.event,
data: creds,
});
})
.catch(function onGetCredentialsError(error) {
console.error('failed to get credentials', error);
evt.ports[0].postMessage({
event: 'reject',
type: evt.data.event,
error: 'failed to get credentials',
});
});
}
window.addEventListener('message', onMessageFromChatBot, false);
Instances of IframeLoader
have the following methods that can be used
to interact with the chatbot UI component postMessage interface:
- waitForChatBotReady: returns a promise that resolves once the chatbot UI is ready. The promise is rejected after a 15 seconds timeout.
- sendMessageToIframe: Used to make API calls from the parent page to the ChatBot UI as a promise. See the Parent to ChatBot UI API section below
- onMessageFromIframe: Handles API calls initiated from the chatbot UI. See the ChatBot UI to Parent API section below
For example, you can use the sendMessageToIframe
method to
programmatically send a text message to the bot using the postText API:
var lexWebUiLoader = new ChatBotUiLoader.IframeLoader();
lexWebUiLoader.sendMessageToIframe({event: 'postText', message: 'order'})
.then(() => console.log('message succesfully sent'))
.catch((error) => console.error(`error ${error}`));
If you want to hack the loader library code under lex-web-ui-loader, you would need to install the project dependencies using npm. The build files are bundled using webpack. The development environment provides a Webpack DevServer configuration to automatically bundle and hot reload changes while you make changes.
# install dependencies
npm install
# serve the sample site with hot reload at localhost:8000
npm run dev
# build the library
npm run build