On this page
When you embed an HTML page on a custom panel, there are a number of additional behaviours you can configure.
Preserving
When a page is embedded as as an HTML page, it will be refreshed whenever the data in the app changes. This can be disconcerting and interrupt the user experience. To mitigate against this, you can set the preserve
parameter to true
. This will leave the page alone and instead send notifications to the page with the data update.
return {
url: "https://<url-to-page>/index.html",
method: "pipe",
display: "embed",
preserve: true,
...
}
In the JavaScript on the HTML page, the updates are handled by:
if (event.data.startsWith("update:")) {
// An event to say the parent window wants to update the contents of the page, as feedback has changed
// will only get fired if 'preserve' is set to true in the feedback configuration.
var json = JSON.parse(event.data.substr(7));
handleCommand(json);
return;
}
Listening for events
Pages that are launched in pipe
mode can opt in to be notified of all types of events happening in the main application. To be notified, the script must include the list of events to be notified on.
return {
url: ".../recorder.html",
method: "pipe",
display: "launch",
preserve: true,
query: null,
notify: {
protocol: "postmessage",
receiveVerbs: [],
sendVerbs: [
"operation-selected",
"operation-completed",
"operation-started",
"selection-changed",
"environment",
"launch-init",
"extent"
]
}
};
Sending messages from an HTML page to a Sweet application
When a web page is launched in pipe
mode, it can send messages back to the application. There is a large set of messages that can be run.
Below a few examples of the messages which can be run. Further examples can be found in the application actions section.
// source and origin have been stored from the first call to receiveMessage
source.postMessage(
JSON.stringify({
action: "command",
command: {
command: "panto",
geometry: { x: 100, y: 100, spatialReference: { wkid: 102100 } }
}
}),
origin
);
A common workflow is to add an action, which can then be run from web page, using the following script:
source.postMessage(
JSON.stringify({
action: "command",
command: {
command: "run-action",
// The name of the action in the script
name: "Name Of Action",
// If the action requires parameters, provide them here
params: {
paramName: "paramValue"
}
}
}),
origin
);
The action can be hidden. The panel the action is on can be hidden with enable/disable capability. This can be done as a startup action.
The web page can also send multiple commands to be run:
source.postMessage(JSON.stringify({
action: "command",
command: {
JSON.stringify({
action: "command",
// Note command is an array
command: [
{ "command": "openpanel", "panel": "select" },
{ "command": "close-webpage" }
]
}), origin);
Resizing and embedded HTML pages
Embedded HTML pages manage their own size. It is often a requirement for the page to notify the parent application about what height it needs to be.
When the page is first initialised, a message is received with a unique ID. This can be used to notify the parent to resize the page.
The following is an example which dynamically resizes the page according to the contents of the web page:
function receiveMessage(event) {
if (event.data.startsWith("notifysizing:")) {
// Event to say the parent window wants to be notified of size changes to the page
trackSizing(event.data.replace("notifysizing:", ""), event.source, event.origin);
return;
}
...
}
// Function which tracks the true size of the page
// and sends message to the parent window, to resize the iframe
// Needed to ensure scrollbars do not appear
function trackSizing(id, source, origin) {
function onElementHeightChange(elm, callback) {
var lastHeight = elm.clientHeight, newHeight;
(function run() {
newHeight = elm.clientHeight;
if (lastHeight != newHeight)
callback(newHeight);
lastHeight = newHeight;
if (elm.onElementHeightChangeTimer) {
clearTimeout(elm.onElementHeightChangeTimer);
}
elm.onElementHeightChangeTimer = setTimeout(run, 200);
})();
}
onElementHeightChange(document.body, function (newHeight) {
source.postMessage(JSON.stringify({ action: "resizeframe", id: id, height: newHeight }), origin);
});
source.postMessage(JSON.stringify({ action: "resizeframe", id: id, height: document.body.clientHeight }), origin);
}
Querying a layer in Sweet from a web page
If a pipe
has been established between the web page and the Sweet application, then queries can be sent through.
To send a layer query to Sweet, the following syntax can be used:
var queryid = 1; // Unique ID. Must be set different for every query
source.postMessage(
JSON.stringify({
action: "query",
query: {
queryid: queryid, // Unique ID to
layername: "Crops",
layerid: "",
where: "1=1",
num: 10,
orderByFields: null,
spatialRelationship: "",
geometry: null
}
}),
origin
);
The data response for this query will be send back via a postMessage. This must be picked up by the receiveMessage.
function receiveMessage(event) {
...
// Check for query response
if (event.data.startsWith("QUERY_RESPONSE:")) {
// Get the query response
var queryresponse = JSON.parse(event.data.substr("QUERY_RESPONSE:".length));
// The query response will have the queryid
finishQuery(queryresponse)
return;
}
...
}
Export to CSV
// Return the export CSV command
function buildCSVCommand(reportFeatureSet) {
// Convert report featureset into a csv format
var csvDataForExport = CSV(reportFeatureSet);
// Return encoded HTML Attribute for exporting a featureSet.
// The `csv` parameter must contain the csv content to be exported.
// The `filename` parameter is optional - if supplied it is the suggested
// name of the CSV file when downloaded - the default is `data.csv`
return EncodeHtmlAttribute(Text({
command: "export-csv",
csv: csvDataForExport,
filename: "exported-data.csv",
}));
}
Then reference it in the HTML like this:
<div data-run-commands="${buildCSVCommand(reportFeatureSet)}" class="btn btn-outline-primary print:hidden">Export</div>