Feature: Use JavaScript with embedded apps

Host pages can now interact with their embedded apps through our new JavaScript library. For instance, host pages can now get all values of an app through the library. We expect this feature to be used by third-party services wishing to integrate with Calcapp.

Almost exactly three years ago, we enabled apps to be shared with others. From that point on, app authors were able to embed their shared apps in websites, but apps have until now been islands unto themselves, unable to communicate with the host page.

That changes with this release. We’re now introducing the calcapp-iframe.js JavaScript library, which can be used to extract values from apps you embed.

We don’t expect most Calcapp authors to make direct use of this library. Using it, for the most part, requires traditional programming skills and part of Calcapp’s appeal, of course, is that we only require a familiarity with formulas and not traditional programming.

(There is one way to use the library without doing traditional programming, though. Our new auto-height feature only requires you to add a few attributes to your HTML markup to enable the feature. Behind the scenes, the JavaScript library is used.)

Most people making use of this library, at least initially, will be programmers working to integrate the services their companies provide with Calcapp. Indeed, that was our impetus for creating calcapp-iframe.js — an app author wanted to enable the app he created to talk to his hosted payment page and he asked us to work together with his payment provider. We learned what we had to make happen in order for them to create their Calcapp integration and the result is this library.

The rest of this blog post is an in-depth technical description of how programmers can make use of this library. Don’t worry — we haven’t switched our focus to building a tool for traditional programmers! We’ll be back soon with new features geared completely towards no-code app development.

Using the library from JavaScript

The library must be loaded from https://connect.calcapp.net/calcapp-iframe.js or from https://connect.calcapp.net/calcapp-iframe.min.js. (The latter address if for the minified version, which is smaller and downloads faster but does not include any documentation.) Please don’t self-host the library; we may make changes to it from time to time as Calcapp evolves. Add the defer attribute to the script tag you use to load the library to prevent it from slowing down your page as it loads.

A number of features are used which are not necessarily supported by older browsers, notably Internet Explorer. To make this library work for users on older browsers, a number of polyfills need to be loaded. (A polyfill is an implementation of a browser feature in JavaScript, often with fewer features or worse performance than the corresponding feature offered natively in the browser.)

To generate markup including appropriate polyfills, use the new embed tab of the window which appears when you share an app and click Support Internet Explorer:

The embed tab of the Share dialog

This library sets the calcappIframe property of the window object to an instance which serves as the entry point to this library. Host pages can use this object to retrieve values from the host app and to ask this library to automatically manage the dimensions of iframe elements hosting apps written with Calcapp.

For instance, to print the value of field named Field1, part of a panel named MainPanel, to your JavaScript console, use this code:

calcappIframe.getValue("MainPanel", "Field1").then(function(value) {
  console.log("Value: " + value);
}).catch(function(error) {
  console.error("Error: " + error);
});

These functions are part of the calcappIframe object:

calcappIframe.getValue

Returns a promise which is resolved with a value from an app built with Calcapp and hosted by an iframe element, or is rejected with an error message if there is a problem. To print the value of field named Field1, part of a panel named MainPanel, to your JavaScript console, use this code:

calcappIframe.getValue("MainPanel", "Field1").then(function(value) {
  console.log("Value: " + value);
}).catch(function(error) {
  console.error("Error: " + error);
});

Use the getValues function instead of this function if multiple values need to be retrieved.

Calculations, variables and properties

Values in an app built with Calcapp are stored by properties, which belong to either variables or calculations. Fields, text boxes, groups, buttons and list panel items are all variables. Fields offer a default property which holds the field value, but also properties such as visible (determining whether the field is visible), label (the label of the field), initialValue (the value displayed initially for the field), formattedValue (the field value formatted as a string) and backgroundColor (the background color of the field). Number fields, text fields, switch fields and date and time fields support properties specific to them. Similarly, list panel items, text boxes and groups support their distinctive properties.

All panels are calculations. Panels feature properties such as primaryColor (the color which, if not overridden, is used for the navigation bar background, button labels and more), fieldValueColor (the color used for field values) and nextPanelAvailable (whether the user should be allowed to navigate forward to the next panel).

There is a top-level calculation which features properties such as zoomLevel (a value determining the visual size of the app), userEmailAddress (the email address of the user signed into a private app) and operatingSystem (the operating system used on the device running the app). This calculation is normally named App, if not overridden by the app author.

All properties are documented in Calcapp Creator’s reference sidebar. Unlike this documentation, though, property names provided to this function must use a lower-case letter for the first letter of the property name. (Use visible and not Visible.)

Calcapp types in JavaScript

Calcapp numbers are mapped to JavaScript numbers, Calcapp logical values are mapped to boolean values and Calcapp text strings are mapped to JavaScript strings. Blank values in Calcapp are mapped to null values in JavaScript. Runtime errors (such as division by zero errors) are mapped to objects whose error property is true.

The returned promise

This function returns a Promise object, which represents a future value. Apps written with Calcapp run as part of iframe elements and the only way for a page embedding such apps to communicate with them is through message passing. As such, values cannot be returned directly (synchronously) and are only available at some unspecified point in the future. The returned promise is said to be resolved when the value becomes available, at which point client-provided code can run. This code is given as a function to the then() function of the promise object:

calcappIframe.getValue("SomePanel", "SomeField").then(function(value) {
  console.log("Value: " + value);
});

Errors should be handled by providing a function to the catch() function of a promise object:

calcappIframe.getValue("SomePanel", "SomeField").then(function(value) {
  console.log("Value: " + value);
}).catch(function(error) {
  console.error("Error: " + error);
});

Samples

To get the value of the field Field1 belonging to the calculation panel MainPanel, use the following code (provided that there is only one iframe element in the document hosting an app written with Calcapp):

calcappIframe.getValue("MainPanel", "Field1").then(function(value) {
  console.log("Value: " + value);
}).catch(function(error) {
  console.error("Error: " + error);
});

To determine whether the field Field1, belonging to the calculation panel CalculationPanel1, is visible, use the following code:

calcappIframe.getValue("MainPanel",
                       "Field1",
                       "visible").then(function(value) {
  console.log("Value: " + value);
}).catch(function(error) {
  console.error("Error: " + error);
});

If there are multiple apps embedded in a single page, the app must be identified. Clients can pass either the iframe element hosting the app directly as the fourth parameter, the alias of the app (such as (abc123) or the numeric position of the iframe element hosting the app (in depth-first traversal order). Here, an app alias is given:

calcappIframe.getValue("MainPanel",
                       "Field1",
                       "visible",
                       "abc123").then(function(value) {
  console.log("Value: " + value);
}).catch(function(error) {
  console.error("Error: " + error);
});

To read the email address of the user signed into a private app, use this code (provided that the top-level calculation has not been renamed):

calcappIframe.getValue("App",
                       null,
                       "userEmailAddress").then(function(value) {
  console.log("Value: " + value);
}).catch(function(error) {
  console.error("Error: " + error);
});

Parameters

            calcappIframe.getValue(calculationName,
                       variableName,
                       propertyName,
                       app)
calculationName
string
The name of the calculation which the identified variable or property belongs to. Panels are calculations.
variableName
string
(optional)
The name of the variable which the identified property belongs to. Fields, text boxes, buttons, groups and list panel items are all variables. If this parameter is left undefined, the identified property is expected to reference a calculation property as opposed to a variable property.
propertyName
string
(optional)
The name of the property whose value should be retrieved. If this parameter is left undefined, the value of the default property is returned instead. For fields, the default property is the value of the field. The first character of a property name must be lower-case.
app
HTMLIFrameElement
or string
or number

(optional)
The app the value should be retrieved from. If this parameter represents an iframe element, the value is retrieved from the app hosted by said element. If this parameter represents the number n, the value is retrieved from the app hosted by the n-th iframe element in the document hosting an app written with Calcapp, in depth-first traversal order. If this parameter represents a string, this string is expected to be an alias of an app written with Calcapp, and the value is retrieved from the app hosted by an iframe element in the document referencing the app with the given alias. If this parameter is undefined and there is only a single iframe element in the document hosting an app written with Calcapp, the value is retrieved from this app. If this parameter is undefined and there are multiple iframe elements hosting apps written with Calcapp, an exception is thrown.
Returns
Promise
A promise which is resolved with the sought value or is rejected with an error message if there is a problem.
Throws
Error
  • If there are no iframe elements in the document hosting apps written with Calcapp.
  • If the given app parameter is a number and said number is greater than or equal to the number of iframe elements in the document hosting apps written with Calcapp.
  • If the given app parameter is a string and there is no iframe element in the document which hosts an app with an alias equal to the given parameter.
  • If the given app parameter is defined but is neither a number, nor a string, nor an HTMLIFrameElement element.

calcappIframe.getValues

Returns a promise which is resolved with one value or multiple values from an app built with Calcapp and hosted by an iframe element, or is rejected with an error message if there is a problem. To print the value of field named Field1, which is part of a panel named MainPanel, as well as whether it is visible, use this code:

  calcappIframe.getValues([{
    calculationName: "MainPanel",
    variableName: "Field1"
  }, {
    calculationName: "MainPanel",
    variableName: "Field1",
    propertyName: "visible"
  }]).then(function(values) {
    console.log("Field value: " + values[0]);
    console.log("Is the field visible?: " + values[1]);
  }).catch(function(error) {
    console.error("Error: " + error);
  });

This library communicates through message passing with the app hosted by an iframe element. As such, retrieving multiple values through this function is far more efficient than repeatedly invoking the getValue function.

For information on the Calcapp concepts discussed here, refer to the getValue documentation.

Parameters

            calcappIframe.getValues(soughtValues, app)
soughtValues
Array
An array of objects which hold information on a sought value. This object must contain a property named calculationName and can optionally contain properties named variableName and propertyName. These properties are identical to the calculationName, variableName and propertyName parameters given to the getValue function; refer to its documentation for more information. The resolved value of the returned promise corresponds to the array given here; refer to the documentation for the return value for more information.
app
HTMLIFrameElement
or string
or number

(optional)
The app the values should be retrieved from. If this parameter represents an iframe element, the values are retrieved from the app hosted by said element. If this parameter represents the number n, the values are retrieved from the app hosted by the n-th iframe element in the document hosting an app written with Calcapp, in depth-first traversal order. If this parameter represents a string, this string is expected to be an alias of an app written with Calcapp, and the values are retrieved from the app hosted by an iframe element in the document referencing the app with the given alias. If this parameter is undefined and there is only a single iframe element in the document hosting an app written with Calcapp, the values are retrieved from this app. If this parameter is undefined and there are multiple iframe elements hosting apps written with Calcapp, an exception is thrown.
Returns
Promise
A promise which is resolved with an array of the sought values or is rejected with an error message if there is a problem. The element at index n of the resolved array corresponds to the element at index n of the array given as the soughtValues parameter.
Throws
Error
  • If there are no iframe elements in the document hosting apps written with Calcapp.
  • If the given app parameter is a number and said number is greater than or equal to the number of iframe elements in the document hosting apps written with Calcapp.
  • If the given app parameter is a string and there is no iframe element in the document which hosts an app with an alias equal to the given parameter.
  • If the given app parameter is defined but is neither a number, nor a string, nor an HTMLIFrameElement element.

calcappIframe.subscribeToNewHeight

Starts subscribing to new height events from an app built with Calcapp and hosted by an iframe element and returns a function which, when invoked, ends the subscription. A new height event is communicated when an app starts and when a user takes an action such that the height of the app changes. Such actions include navigating to a new panel and entering a value in a field such that a different field is made visible.

To have this library automatically adjust the height of an iframe element hosting an app written with Calcapp, use the function manageHeight instead.

Sample

This sample subscribes to new height events and prints the new height to the console:

calcappIframe.subscribeToNewHeight(function(newHeight, iframe) {
  console.log("New height: " + newHeight);
});

Parameters

            calcappIframe.subscribeToNewHeight(listener, app)
listener
Function
A function which is invoked with the new height, in pixels, passed as its first parameter. The iframe element hosting the app written with Calcapp is passed as the second parameter.
app
HTMLIFrameElement
or string
or number

(optional)
The app whose events should be subscribed to. If this parameter represents an iframe element, the app hosted by this element is used as the subscription target. If this parameter represents the number n, the app hosted by the n-th iframe element in the document hosting an app written with Calcapp, in depth-first traversal order, is used as the subscription target. If this parameter represents a string, this string is expected to be an alias of an app written with Calcapp, and the app hosted by an iframe element in the document referencing the app with the given alias is used as the subscription target. If this parameter is undefined and there is only a single iframe element in the document hosting an app written with Calcapp, this app is used as the subscription target. If this parameter is undefined and there are multiple iframe elements hosting apps written with Calcapp, an exception is thrown.
Returns
Function
A function which, when invoked, ends the subscription.
Throws
Error
  • If there are no iframe elements in the document hosting apps written with Calcapp.
  • If the given app parameter is a number and said number is greater than or equal to the number of iframe elements in the document hosting apps written with Calcapp.
  • If the given app parameter is a string and there is no iframe element in the document which hosts an app with an alias equal to the given parameter.
  • If the given app parameter is defined but is neither a number, nor a string, nor an HTMLIFrameElement element.

calcappIframe.manageHeight

Starts automatically adjusting the height of the iframe element hosting the given app in response to the app changing its height and returns a function which, when invoked, stops the process. The app typically changes its height in response to the user navigating to a new panel or entering a value in a field such that a different field is made visible.

This function uses a mutation observer to automatically stop watching the height of an app when the iframe element hosting it is removed.

Options

The second parameter is expected to be an object, with properties customizing the behavior of this function.

To ensure that the height of an iframe element is not set smaller than a certain value, set the minimumHeight property to the desired minimum height, in pixels. Similarly, to ensure that the height of an iframe element is not set larger than a certain value, set the maximumHeight attribute to the desired maximum height, also in pixels.

Embedded apps and host pages communicate asynchronously. When the height of an app changes using an animation, this library animates the iframe element to match the app height. When the app becomes larger, for instance, the iframe element may, at times, lag behind the embedded app and momentarily not be large enough to fully contain said app. As a result, a scrollbar may appear, only for it to disappear a split second later when the iframe element is properly sized.

To prevent this situation from occurring, iframe elements should not be sized such that they precisely fit the embedded app. Rather, they should be a number of pixels taller than the app. To achieve this, set the extraHeight property to the number of pixels which should be added to the height of the app. The default value is 16 pixels, which is appropriate for most apps. For a perfectly snug fit, set this value to zero.

Declarative usage

This function can also be used declaratively, by adding the attribute calcapp-managed-height to the iframe element. The values passed through the options parameter can also be set this way. Use the calcapp-managed-height-minimum attribute for the minimumHeight option, calcapp-managed-height-maximum for the maximumHeight option and calcapp-managed-height-extra for the extraHeight option. The data- prefix may be used for all attributes.

When used declaratively, this function is called by this library in response the DOMContentLoaded event firing on the window object. As such, using this method declaratively is only possible for iframe elements present when the page is initially loaded.

Sample

This sample asks the library to manage the height of an app written with Calcapp:

calcappIframe.manageHeight("abc123", {
  minimumHeight: 200,
  maximumHeight: 400,
  extraHeight: 50
});

Parameters

            calcappIframe.manageHeight(app, options)
app
HTMLIFrameElement
or string
or number

(optional)
The app whose events should be subscribed to. If this parameter represents an iframe element, the app hosted by this element is used as the subscription target. If this parameter represents the number n, the app hosted by the n-th iframe element in the document hosting an app written with Calcapp, in depth-first traversal order, is used as the subscription target. If this parameter represents a string, this string is expected to be an alias of an app written with Calcapp, and the app hosted by an iframe element in the document referencing the app with the given alias is used as the subscription target. If this parameter is undefined and there is only a single iframe element in the document hosting an app written with Calcapp, this app is used as the subscription target. If this parameter is undefined and there are multiple iframe elements hosting apps written with Calcapp, an exception is thrown.
options
Object
An object containing various options for this function, as described above.
Returns
Function
A function which, when invoked, stops watching the height.
Throws
Error
  • If there are no iframe elements in the document hosting apps written with Calcapp.
  • If the given app parameter is a number and said number is greater than or equal to the number of iframe elements in the document hosting apps written with Calcapp.
  • If the given app parameter is a string and there is no iframe element in the document which hosts an app with an alias equal to the given parameter.
  • If the given app parameter is defined but is neither a number, nor a string, nor an HTMLIFrameElement element.
« Build your startup on Calcapp Feature: Auto-height for embedded apps »