Rich Message Studio components and conditional logic
Note: This topic contains technical reference information for template builders and developers.
Whispir’s Rich Message Studio is where you create rich messages using a library of customised template components. (For example, components that let you add text, images, buttons, maps and videos players to your messages.)
This topic provides technical information about the components developed by Whispir that can easily be imported into the Rich Message Studio in the Whispir platform.
See also Use the rich message/template components and Use the Shared Template Styles components.
Components are extensible ‘snippets’ of content that form the basic building blocks of Whispir’s rich messages. Any number of components can be assembled to create rich messages and interactive mini-applications, which are then served by the Whispir platform.
Existing components can be extended and new ones can be built from the ground up, giving template builders and developers the facility to create any component they can imagine.
Components are defined using languages and tools that developers use today: HTML, Javascript and JSON.
Whispir provides a number of default components out-of-the-box. These help with basic authoring and act as a starting point for building your own components.
- Components can be used by dragging and dropping them from the Components Library onto the message (or template).
- Any component added to a message can then be modified by hovering over it and clicking the edit icon .
- Editing a component displays the component’s properties in the editing pane on the right. For example, an image component has properties that let you choose the image to be displayed and modify its height and width.
Components are structured in the following way:
- The basic building block of a component is markup, which can be any type of displayable content such as plain text or rich HTML. When used in rich messages, markup can also contain inline CSS and Javascript to build interactive components.
- Each markup building block can optionally include one or more elements. An element defines any references used in the markup, such as a paragraph of HTML, an image, a file or a text field.
- Each element must contain one or more properties, which define name–value pairs of data to drive the element; for example, an image URL or image description.
The following sample structure illustrates this relationship:
- Markup (HTML)
- Element (Image)
- Property (URI)
- Property (Height)
- Element (Paragraph)
- Property (RichText)
Components provide developers a ‘build once, re-use many times’ approach to building templates and HTML5 apps.
Example:
You can access a component’s markup source and properties by clicking Code in the editing pane when you’re editing the component in Whispir’s Rich Message Studio.
Components can be built using only markup. For example, entering the following HTML code on the Markup tab (accessible once you’ve clicked Code) will render the result in the component:
<h1>Hello World</h1>
<p>It's a wonderful day</p>
However, editing markup directly makes it difficult for anyone other than a developer to modify this component.
This is where elements come in. We can define an element of ‘RichText’ type in the component, which will then display a text editor in the left sidebar, allowing anyone to edit the text.
Enter the following JSON code on the Properties tab (accessible once you’ve clicked Code):
[
{
"element": {
"name": "Hello World Paragraph",
"reference": "paragraph",
"properties": [
{
"property": {
"type": "RichText",
"name": "Hello World Paragraph",
"value": "Hi right back, <b>World</b>!",
"reference": "mytext"
}
}
]
}
}
]
The markup is now updated to reference the element:
<h1>Hello World</h1>
<p>[[paragraph.mytext]]</p>
Now when editing this component a text editor will appear, allowing anyone to easily update the paragraph of text.
Markup
You can include any valid HTML, inline Javascript or inline CSS.
<div class="myDiv">
<img class="myImg" src="[[photo.src]]"/>
<p>[[paragraph.mytext]]</p>
<div>
<style>
.myImg { width: [[photo.width]]; }
.myDiv { text-align: center; margin: 10px;}
</style>
Properties
JSON array of element object(s):
[
{
"element": {
"name": "Profile Photo",
"reference": "photo",
"properties": [
{
"property": {
"type": "Image",
"name": "Profile Photo",
"value": "",
"reference": "src"
}
},
{
"property": {
"type": "Text",
"name": "Width (in %)",
"value": "50%",
"reference": "width"
}
}
]
}
},
{
"element": {
"name": "Profile Description",
"reference": "paragraph",
"properties": [
{
"property": {
"type": "RichText",
"name": "Profile Details",
"value": "My <b>details</b>",
"reference": "mytext"
}
}
]
}
}
]
Scripts
JSON array of Javascript resource URLs:
[
"http://content.whispir.com/public/template/lib/plugins/v1.6.js",
"https://maps.googleapis.com/maps/api/js"
]
Note: These are external javascript resources that are loaded in the head tag of a rich message.
How properties work
Properties provide an easy way for developers to expose features of their components in the editing pane in a rich message.
These features might include:
- A body of text that can be edited and formatted in an editor
- An image to be displayed, including its height and width
- A link to a video
- A location to be displayed on a map.
For example, you have developed a component that displays an image with a caption. Instead of replacing the image manually in the HTML or editing the text inline, you can expose these as Image and Text property types in your markup. When someone uses your component, they can browse for an image or edit the text caption without editing the HTML directly.
Property types
Properties are defined using the following primary types:
Type | Displays in the editing pane | Accepts |
---|---|---|
Text |
Small plain text input field |
Any text string |
PlainText |
Plain text editor |
Body of plain text |
RichText |
Rich HTML text editor |
Rich text, basic HTML markup |
Image |
Widget to upload an image or select one from the Resource Library |
Any image resource |
Resource |
Widget to upload a file, such as a CSS or PDF file |
Any text/binary file |
DropList |
A drop-down list that provides two or more options to select |
Array of strings |
Property samples
The following is a sample of all properties used within a Customer Profile element:
[
{
"element": {
"name": "Customer Profile",
"reference": "customerprofile",
"properties": [
{
"property": {
"name": "Customer Name",
"value": "Jack Citizen",
"type": "Text",
"reference": "name"
}
},
{
"property": {
"name": "Customer Photo",
"value": "",
"type": "Image",
"reference": "photo"
}
},
{
"property": {
"name": "Customer Type",
"value": "VIP",
"type": "Droplist",
"options": ["VIP","Regular"],
"reference": "type"
}
},
{
"property": {
"name": "Customer Description",
"value": "A VIP Customer who is always welcoming",
"type": "PlainText",
"reference": "description"
}
},
{
"property": {
"name": "Customer Invoice",
"value": "",
"type": "Resource",
"reference": "invoice"
}
},
{
"property": {
"name": "Message to Customer",
"value": "<p><b>Details</b></p><p>Invoice details</p>",
"type": "RichText",
"reference": "msg"
}
}
]
}
}
]
Referencing properties
You can reference properties in any content placed on the Markup tab, as follows:
[[element_name.property_reference]]
Using the previous Customer Profile example (above):
<div class="customerinvoice">
<h1>Your Invoice</h1>
<p>
<img src="[[customerprofile.photo]]">
</p>
<p>
Hi <strong>[[customerprofile.name]]</strong>
</p>
<p>
[[customerprofile.msg]]
</p>
<p>
{{#if-cond '[[customerprofile.type]]' '==' 'VIP'}}
Thanks for your continued loyalty!
{{/if-cond}}
</p>
<p>
<a href="[[customerprofile.invoice]]">Access Your Invoice</a>
</p>
</div>
<style>
.customerinvoice {
color: #888;
margin: 20px;
font-family: sans-serif;
}
img {
width: 100%;
}
</style>
Using component IDs to provide uniqueness
All components within a template have a unique id, which is generated at runtime. The ID allows the same component to be used more than once within the same template.
This is useful if your markup references CSS styles, HTML elements, or Javascript variables that require a local scope.
The ID can be included within the markup using the [[id]] value, as follows:
<img id="photo_[[id]]" src="[[photo.src]]"/>
<style>
#photo_[[id]] { width: [[photo.width]] }
</style>
<script>
var myPhoto[[id]] = document.getElementById("photo_[[id]]");
</script>
This will be rendered at runtime as:
<img id="photo_654321" src="foobar.jpg"/>
<style>
#photo_654321 { width: 50% }
</style>
<script>
var myPhoto654321 = document.getElementById("photo_654321");
</script>
<div class="map">
<p>How to get to
[[loc.address]]
</p>
<div id="map-container-[[id]]"></div>
</div>
<style>
.map {
margin: 0 auto;
padding: 8px;
text-align: center;
}
#map-container-[[id]] {
text-align: center;
margin: 0 auto;
background-color: #eee;
width: 100%;
height: 200px;
left: 0;
top: 0;
display: block;
border: 1px solid #ccc;
max-width: 600px;
}
</style>
<script>
// Show Map based on an address
// ---------
Whispir.plugins.showMap.init({
address: '111 Exhibition Street, Melbourne, VIC',
// Default address used if none found in body of HTML
addressElementId: 'address-[[id]]',
// Source element id which contains destination address text
mapElementId: 'map-container-[[id]]',
route: [[loc.route]]
// true: prompt user location, and route to destination address.
});
</script>
Properties
[
{
"element": {
"name": "Map of destination",
"properties": [
{
"property": {
"name": "What is the address?",
"value": "360 Collins Street, Melbourne",
"type": "Text",
"reference": "address"
}
},
{
"property": {
"name": "Show route to the Person?",
"value": "false",
"type": "Text",
"reference": "route"
}
}
],
"reference": "loc"
}
}
]
Scripts
[
"http://content.whispir.com/public/template/lib/plugins/v1.6.js"
]
Conditional logic allows content to be dynamically shown or hidden, based on the presence of a value in a supplied variable or field. Hidden content is visible to the message/template author but not to the message recipient.
Logic/functional operators
if
This basic conditional operator is used to test if an attribute has any value. If this attribute has a value, it returns content specified within a block.
{{#if @@attribute@@}} exists and has content {{/if}}
if-cond
This conditional operator is used to perform a string comparison of an attribute’s value. If true, it returns content specified within a block.
{{#if-cond @@attribute@@ '==' 'true'}} Yes, it's true {{/if-cond}}
Note: if-cond supports, '==' equal to, and '~' for contains searches.
each
This iterator is used to traverse an object or array. It returns access to child attributes within a block. Order is preserved; there is no sorting.
{{#each @@manifest@@}} @@timestamp@@ - @@note@@ {{/each}}
Limits of conditional logic
- All attribute values are either a String type or an Object of strings type.
- Conditional logic only supports direct string comparisons.
Conditional logic example
Sample attribute data
[{
"fullname": "Jacky Smith",
"email": "jsmith@whispir.com",
"mobile": "0419111222",
"num_days": "4",
"workcentre_name": "Hawthorn",
"new_customer": "true",
"addressee_only": "false",
"manifest":
[{
"name": "Job Manifest",
"value": [{"timestamp":"21-10-2019 10:15","note":"Req. Start Time"},
{"timestamp":"21-10-2019 16:00","note":"End Time"}]
}]
}]
Sample conditional logic in template (plain email shown here)
Hi @@fullname@@,
You have a job to process within @@num_days@@
days at @@workcentre_name@@.
{{#if-cond @@new_customer@@ '==' true}}
See the check-in desk on arrival.
{{/if-cond}}
{{#if-cond @@addressee_only@@ '==' true}}
Addressee only.
{{/if-cond}}
{{#if @@id_required@@ }}
ID required.
{{/if}}
Details:
{{#each @@manifest@@}}
@@timestamp@@ - @@note@@
{{/each}}
See @@web_link@@ for more information.
Output
Hi Jacky Smith,
You have a job to process within 4 days at Hawthorn.
See the check-in desk on arrival.
Details:
21-10-2019 10:15 - Req. Start Time
21-10-2019 16:00 - End Time
See http://customer.au.whispir.com/s/aYid781 for more information.