1. 1 Getting Started
    1. 1.1 Signing Up
    2. 1.2 Logging In
    3. 1.3 Resetting Your Password
    4. 1.4 Personalizing your Account
  2. 2 Using Apps
    1. 2.1 Subscribing to an App
    2. 2.2 Assigning Users and Roles
    3. 2.3 Starting an App
  3. 3 Building Apps
    1. 3.1 Overview
    2. 3.2 The Wizard
      1. Step 1 - Models
      2. Step 2 - Relations
      3. Step 3 - Roles
      4. Step 4 - User Interfaces
    3. 3.4 Models
      1. What Are Models?
      2. Creating a Model
      3. Adding Relations
    4. 3.5 Rules
      1. Data Change
      2. REST
      3. Scheduled
    5. 3.7 Actions
      1. Addressing
      2. Email & SMS
      3. Push
        1. Buttons
        2. Badge
      4. Script
      5. Variables
    6. 3.8 Scripts
      1. 3.8.1 QuickActions
      2. 3.8.2 Logic
        1. Logic Block Types
      3. 3.8.3 JavaScript
        1. Javascript API for User Interface Programming
          1. Using Mobile Plugins
          2. Mobile Plugins
          3. Barcodescanner
        2. Javascript API for Server-side Programming
          1. The Context API
          2. Record
          3. Result
          4. HTTPResponse
          5. ServiceRequest
          6. ServiceResponse
    7. 3.9 Roles
      1. What are roles?
    8. 3.10 User Interfaces
      1. The User Interface Editor
    9. 3.11 Views
      1. The View Editor
    10. 3.12 Forms
      1. What is a form?
      2. Widgets & Forms
    11. 3.13 Templates
      1. What are Templates?
      2. Template Example
      3. Template Helpers
    12. 3.14 Using the Messagebus
  4. 4 Internet-of-Things
    1. 4.1 Provisioning of devices
    2. 4.2 Managing devices
    3. 4.3 Building IoT Applications
      1. 4.3.1 The DeviceContext API
      2. 4.3.2 The GPIO Module
        1. GPIO Pin
        2. GPIO Input Pin
        3. GPIO Output Pin
        4. GPIO Input/Output Pin
      3. 4.3.3 The I2C Module
        1. I2C Device
      4. 4.3.4 The SPI Module
        1. SPI Device
  5. 5 App Builder Training
    1. 5.0 Introduction
    2. 5.1 Data Models
    3. 5.2 Views, Forms & Widgets
    4. 5.3 DataGrids & Basic Navigation
    5. 5.4 Rules, Actions & Templates
    6. 5.5 Relations
    7. 5.6 Filtering Data with Queries
    8. 5.7 The Navbar Menu

1 Getting Started

Welcome to the appivo documentation-site. This is the place to find details regarding the concepts involved when using and/or building applications based on the Appivo platform. If you can’t find an answer to what you are looking for please visit our community forum.

The perhaps quickest way to get acquainted with the appivo-platform is to checkout our video-tutorials. They are linked to from relevant places like the builder itself – check the help-link in top right corner of the screen. Here’s also an index of them all. It’s highly recommended to watch a couple to get started and then use them as a reference as needed.

Beginner Tutorials

  1. Introducing the Appivo-platform. This video shows the building-blocks of an application but doesn’t go into any details.
  2. Building a simple app using the AppWizard. This video shows how to build a very simple application using the AppWizard.
  3. Creating a simple sensor-application This video shows how to build a very simple internet-of-things sensor-application.

1.1 Signing Up

Signing up with Appivo is easy! Just visit the signup page, enter the requested information (your first- and last name, your country, a valid email-adress, and your organization’s name), and click the “Sign Up” button. An activation email will be sent to the email adress you specified.

The activation email contains a link that takes you to the password selection page. Once you have specified your desired password, your account is activated and you will be taken to the login page where you can login

If you cannot find the activation email:

  • Check your spam folder, occasionally the email ends up there.
  • Sometimes it can take a minute or two for the mail to arrive, wait a bit and see if it arrives.
  • Send out another activation email by following the Resetting your Password guide.

You can also signup using your Facebook or LinkedIn account. On the signup page, click either the “Connect with Facebook” or “Connect with LinkedIn” button, fill in the missing details and click sign up. Signing up using Facebook/LinkedIn allows you to login using your Facebook/LinkedIn account.

1.2 Logging In

Log in by specifying your email adress and password. If you have forgotten your password, see Resetting your Password.

If you signed up with Facebook/LinkedIn you can click the “Login with Facebook” or “Login with LinkedIn” buttons to login.

1.3 Resetting Your Password

To reset your password, go to the login page and click “Forgot password”. You will be asked to specify your email adress, after which an activation email will be sent. Clicking the link in the activation email will take you to a password selection page, letting you change your password.

If you cannot find the activation email:

  • Check your spam folder, occasionally the email ends up there.
  • Sometimes it can take a minute or two for the mail to arrive, wait a bit and see if it arrives.
  • Try sending out another activation email.

1.4 Personalizing your Account

2 Using Apps

When logging in to the Appivo platform you will be taken to the “My Apps” screen. This screen displays all the apps you have subscribed to and lets you launch the apps by clicking their icons. At first your “My Apps” screen is probably empty, this is because you have not subscribed to any apps yet.

2.1 Subscribing to an App

Before you can use an app you must subscribe to it. Use the navigation bar at the top of the page to open the marketplace. To subscribe to an app on the marketplace, click on the app, then click on the subscribe button.

Many apps allow their subscribers to configure the app for themselves. To configure an app, open the app in the marketplace and click the “Configuration” tab.

You can also use the marketplace to assign users to your app. Users assigned to the app will be able to see and launch the app from their “My Apps” screen.

Also notice that if your application has IoT deviceprofiles you will need to assign the profiles to devices. This is done under the device-assignment tab where you can enable/disable deviceprofiles and specify what tags a device must have to receive the profile.

2.2 Assigning Users and Roles

After subscribing to an app you should assign users to it. You can assign the users through the marketplace, or use the account page to do it. The “Account Page” is found in the user menu (top right). In the “Account Page”, select the “Users” tab to find the list of all your users. Clicking a user in the list allows you to edit the user’s details, including the apps the user is subscribed to.

When editing a user’s details, you can also add roles to the user. Roles control what a user is allowed to do with the platform and with the apps they are subscribed to. By assigning roles you can designate which users are administrators and which are standard users, for example.

2.3 Starting an App

Subscribed apps are started from the “My Apps” page. Just click the icon of the app you wish to start. Some apps are also available on the Google Play Store / Apple App Store.

3 Building Apps

3.1 Overview

An app is a composed of several parts: Models and records hold the data that your app wants to keep track of. Rules are triggered by certain events in the app and perform arbitrary actions that are defined by you (the developer). Queries can be used to fine-tune access to the models and records, adding powerful filtering and merging capabilities. User interfaces and views are the graphical interfaces that users normally interact with. They contain widgets such as buttons, datagrids, labels and textfields. Similar to rules, widgets can also react to events (like a button click) and perform actions. By using all these features together, you can create powerful and intuitive apps with endless possibilities!

3.2 The Wizard

The Wizard is a short step-by-step guide that allows you to quickly and easily create the foundation for your app. It is the recommended starting point for anyone new to Appivo. Important note: Don’t worry too much about making errors in the Wizard. It is always possible to correct them later on!

A video showing the wizard in action is available here.

Step 1 - Models

This step allows you to define the models of your app. Models describe the data that your app uses. Example models include:

  • Customers
  • Articles
  • Orders
  • Images
  • Schedules

For simplicity there exists several templates that define models for common use cases. For more specific use cases you can create the models yourself. It is also possible to import models. For example, importing the user model from System allows you to bind app-specific data like schedules to users (binding models together is done in the next step, relations).

Models consist of attributes. Each attribute describes some important property (for instance, a customer’s address or phone number or an articles price) and has a data type that defines how the attribute looks and behaves.

Data Type Description Example use cases
Integer A number. Allows arithmetic operations, cannot have any decimals
Float A number. Allows arithmetic operations, can have decimals. Prices
String Plain text. Use for storing text of all kinds. Also for numbers you do not want to perform arithmetic operations on (such as phone numbers).
Boolean A value that can only be true or false. Completion of tasks, calendar bookings, etc.
Date, Time, Date & Time Stores date and/or time in various formats. Start times, completion times, durations, etc.
State Has a finite number of values, defined by you. Order status (placed, in progress, done) and similar

For more information on models, see models

Step 2 - Relations

After defining your models, the next step is to define how they can relate to each other.

For example, it makes sense that customers place orders. This indicates that we should have a relationship between the customer model and the order model. It also makes sense that each order should contain of a number of articles, meaning we should probably also have a relationship between the order and article models.

Every relation has a cardinality that describes how the relation can be bound. For example, a customer can place several orders, but each order can only ever have one customer associated with it (the customer who placed the order). For more information about relations and cardinality, see relations.

Step 3 - Roles

Roles define what users are allowed to do within your app. For example, in a video sharing app, all users should be able to upload videos, but only specific users (the moderators) would be able to delete videos. This step allows you to define multiple roles and declare what each role is allowed to do with each model.

Step 4 - User Interfaces

In this step you will provide some information that the wizard uses to generate user interfaces (UI’s) for your app. You can decide that you only want your app to be accessible from a mobile UI (intended for phones), or maybe that your app is accessible from a mobile UI but any data must be created using a desktop UI (intended for computers). Tick the boxes that you wish and the wizard will use this data, together with the models and relations that you specified earlier and generate views for your app. For more information, see user interfaces and views.

3.4 Models

The most fundamental component in a system that manages information is a description of that information. What exactly is it that your application will manage ? Orders ? Vehicles ? Contacts ?

Telling the system what information we’re dealing with is typically the first step when building an application, all else depends more or less on that. A full description of the pieces of information and how they relate to each other is called an application data model.

An application data model is made up several distinct models where each individual model represents some part of the information you are managing. If you think about the solution that you want to create several nouns will probably come to mind, these nouns – perhaps customer, order, article, adress, invoice etc are likely to be models. A good starting point is to try to understand what different bits of information are involved in your application. It’s important to distinguish between something that is a model and something that is just an attribute of a model.


Lets say that we want to create a simple ordering system – and the words that come to mind are order, article, invoice, customer, adress, city, email-adress, order-date, delivery-date. The first thing to do is to get a grip on how the information relates. For instance – it’s an ordering system so the concept of an order is likely central to the application, so is customer and invoice. Email-adress however is an attribute of a customer – a customer has an email-adress, it also has an adress and an adress consists of several pieces of data – city, state, zipcode, country. It can be a challenge for novices to distinguish between what constitutes a model and what is merely an attribute of a model. Typically something that can be described as “just” a number or piece of text, or a date is an attribute. If you think of the concept “order” it means much more than just a number or a piece of text, although it most definitely would have a special attribute called “number” to represents the order-number, that’s a strong indication that ‘order’ is a model. With the same reasoning – shipping-date is most likely not a model, but an attribute of the order-model.

On the appivo-platform the application data model is managed using the model-editor, it can visualize the data-model for you:

The visual model editor
The visual model editor

The model-editor can also show you a compact list of the models if you prefer. The visual editor is very easy to work with and gives you an immediate picture of how your data is structured. Adding new models is as easy as clicking a button, as is creating relations between models – but more on that later.

Double-clicking a data-model will open up an editor that lets you edit the attributes of that model.

Clicking close to the edge of a data-model and then keeping the mouse-button pressed while dragging the mouse will allow you to create a relationship between to models.

Right clicking on a model will bring up a context-menu with options to edit or delete the model. Deleting a model will automatically also delete all its relationships.

What Are Models?

Models are the basis of almost any app. They describe the data that your app interacts with. Some examples of common models are: customers, receipts, orders, products.
In addition to describing data, models can also have relations between them. For example, an order is usually placed by a customer, which means that the orders and customers models should probably be related.

Creating a Model

Creating a model is done by clicking the New Model button in the model view.




You will be prompted for a name and a type. The name is the model’s unique identifier. The type is one of the following:

  • Data: The model describes some data, this is the standard model type.
    Examples: Customer, Receipt, Order, Product
  • Document: The model describes a file. Use this if you want to store files.
    Examples: Photo, SoundRecording, Signature

After entering the name and type, press OK and your model will be created. Next, it’s time to add some attributes to the model. These attributes are needed for the model to be able to describe something. Adding attributes is done through the model editor, to open it, hold the mouse over your newly created model and click the blue cog that appears (or, from the model list, hold the mouse over the model and click the green pen that appears). Add attributes by dragging them from the right hand side of the screen into the model editor in the centre.




Adding Relations

To add relations, go into the model editor, select the Relations tab (located close to the top of the screen) and click the Add Relation button.




A box will appear, letting you choose which model you wish to relate to. After picking a related model and pressing OK, the relation editor will pop up. The relation editor lets you set some necessary parameters that describe the relation between the two models.

  • Relation Names: The names of the relation on the two models (for example, customer, order, car, owner). The name is only an identifier that is used when referring to the relation.
  • Ownership: Indicates if one of the models is the owner of the other model. A related model cannot exist without it’s owner (for example, an order cannot exist without a customer).
  • Relation Cardinality: Indicates how many records of a particular model the relation can refer to. (for example, a customer may place many orders, but an order can only have a single customer).





3.5 What Are Rules

Rules and actions form a powerful way of making your application dynamic and provides you with a simple way of building logic. A rule is essentially a sort of trigger, there are several kinds of rules which are triggered under different circumstances. We will look each of them below. A rule can be associated with one or more actions, an action is something that happens when a rule triggers. Just like with rules there are several kinds of actions.


In an ordering-system application we want to send an email to the customer when an order has been shipped. We can accomplish that by creating a rule that triggers when an order-record changes status from ‘PENDING’ to ‘SHIPPED’. The rule would be connected to a SendMail-action that will send an email to the customer.

On Data Change Rules

Rules that trigger on data changes are used to react to users creating, editing or deleting records within the app.

On REST Request Rules

Rules that trigger on a REST-request can be used to perform actions at will or to react to external input. This can be used to be able to triggered rules directly from a user interface which normally does not have access to server side data. A REST rule will be triggered any time  the platform receives a REST-request on the URL endpoint configured in the rule. For example a rule with the endpoint ‘myrule’ created in an app called MyApp would react to requests to the URL ‘http://apps.appivo.com/api/app/MyApp/service/myrule’. These rules can also be triggered by using a script in a user interface and calling context.invokeService with the name of the rule.

Scheduled Rules

Rules that trigger on a schedule are simply rules that will be triggered based on a schedule. This can for example be used to continually send push notifications to users that have not interacted with the app and tell them that they need to do so.



3.7 What Are Actions

An action is an instruction that will be executed in the platform when it is called upon. An Action can only be triggered by a rule but can be associated with several rules so that several different triggers can result in the same action being performed. When creating an action there are certain variables that are available to you when you are creating your titles and messages (See variables section).

Addressing Actions

When performing an action that will send some sort of message to users you have to specify to whom the message should be sent. This is done by using the addressing selection available on the action page. Selecting a role means that everybody with that role will be sent the message while selecting a user will send the message only specifically to that user. The record and previousRecord references are special in that they only apply under certain circumstances (more info in the variables section).

Email & SMS Actions

Both email and sms actions are simply used to send sms’s and emails to users. They both support using the variables in order to get context specific information in the message and title (no title for sms’s).

Push Actions

Push notifications are a great way to drive user interaction with your app and they support a lot of features. Push notifications can be used instead of sms’s and emails to send messages to users and are more direct than emails and cheaper than sms’s. The only limitation with push notifications is that they require you to create and distribute a hybridapp for your users. When defining a push notification you can decide what you
want to push to the user. The most basic push notification includes only a message and a title. The title is also optional and will be defaulted to the application’s name if left empty. The message and title work in the same way as for emails and sms’s and support using the same variables.

Push Buttons

Buttons in the push message are a great way to let the user instantaneously interact with the app or to send a user to a specific screen of the app when clicked. Buttons are defined by the text displayed on them and a script that will be executed once the user presses the button. If the app is not running when the user presses the button, it will be launched for the user and the script will then execute.

Push Badge

The badge is the number that is displayed on top of the app icon on an iOS device and in the bottom right corner of a push message on Android. The badge is a great way to tell the user that there is information waiting for them in the app. The value to be sent to users as the badge is determined by a script defined as the badge function and its return value will be used as the badge number.

Script Actions

Scripts actions is how you add custom logic to your applications (see scripts section). This is where you can handle REST-requests that have triggered your rules and you can handle records and access control and many other things (see API documentation).

Action Variables

In the message and title fields and when addressing users, there are several variables accessible that relate to how the rule was triggered. The record reference is a reference to the record that was associated with triggering the rule. For example this could be the newly created record if the rule is setup to trigger on creation of a record. The previousRecord reference is only available with a rule that is set to react to after data changes and is only available for addressing an action. The previousRecord reference could for example be used to access the old phone number when a user updates the phone number set on a model. Rules triggered by REST will pass along the data sent in the rest body to the action and this will be accessible in the record reference. The user that was responsible for the rule triggering is also accessible through the ‘user’ variable. Further there are also variables that allow access to the host URL (‘hostURL’), the application config (‘app’) and locales (‘locale’).
All of these variables can be used in a template manner in the body and title. For example if you wish to include the first name and last name of the User that triggered the Rule in the title of a push you could write the following “{{user.firstname}} {{user.lastname}} triggered this!“. Similarly you might have a rule that reacts when a record of a certain model is updated and that model has a relation to the model user. In that case you could access that persons email by writing the following “Contact info email – {{record.User.name}}” (if the relation is called User).



3.8 Scripts

Scripts are use to add custom logic to an app. This can for example be used to define what will happen when a user presses a button in a user interface or to perform a certain operation every time a record of a certain model is created. Adding scripts to your app is very easy and can be done without knowing anything about programming. At the same time, if you know programming, you are free to write advanced scripts to perform complex logic in your app without hindrance. The three different methods
for creating script can be seen as a hierarchy where QuickActions are the quickest and easiest way to perform the most common actions in an app. JavaScript is on the other end of the spectrum allowing you to create advanced actions with full control over what happens. Logic sits in the middle by still being easy to use but still allowing for full control over what the script does. This hierarchy is also visible by the fact that using a simple action also generates all the information needed for the more advanced script creation options. This allows one to use a QuickAction to perform a task and then switch over to using Logic or JavaScript and see how the same task could be performed using thoose methods. It is however not possible to create a script using JavaScript and then switch to Logic and see how it would look there.

3.8.1 What Are QuickActions?

QuickActions are a quick and easy way to perform the most common operation for the context of the script. All QuickActions are created by Appivo and guaranteed to work properly. To use QuickActions you simply open up a script editor in the builder (for example editing the callback for a button in a userinterface) and then select that the script should be defined using QuickActions (the script editor should default to this mode). Once the script editor is set to use QuickActions you simply pick the action
that you wish to perform from the dropdown and then input any parameters needed for the QuickAction.

3.8.2 What is Logic?

Logic allows you to define app logic, more powerful than QuickActions, without writing any code. Resembling a puzzle, the pieces or blocks connect only in ways that make sense. The blocks are parsed and app code is generated.
The blocks are parsed from top to bottom, and have connections above and below that let you place them in the order you want them to be run. Some blocks also have connections to the right. Blocks connected on these right-side connections are called “arguments” and are used to configure or modify the behavior of a block. For example, a “Get user name” block may take a user block as an argument to specify which user’s name it should output. Figuring out which arguments fit in which block may be difficult. Luckily some help is available through tooltips. Just hold the mouse cursor over the colored part of a block and the tooltip will appear. The tooltip will usually have a short description, detailing the blocks purpose, along with information on the argument inputs and output value. Logic Block Types

Blocks can be divided into a few notable types:

  • Instruction blocks represent instructions – for example, a “send mail” block will send an email.
  • Value blocks represent values such as numbers and text strings (like “hello”, for instance). Value blocks are often used as arguments to configure instruction blocks.

Note: Some blocks are both an instruction and a value. In these cases, the instruction normally comes down to finding the value that the block should represent. Arguments can be used to alter the value. For example, a “get name” block may have different values depending on which user is provided as the argument.

  • Expression blocks perform logical and mathematical on their arguments, such as addition, substraction, or resolving whether a statement is true or not.
  • Control flow blocks controls how your puzzle is parsed. These usually take an expression as the argument and directs the parsing on different paths depending on whether the expression is true or not. For example, you may want to display two different messages depending on if a user has a specific role or not, this could be performed with the “if” block.
  • Variable blocks are special blocks that can be used to temporary store values. For example, maybe you want to overwrite the value of a widget, but need its old value for some calculations later in the puzzle, by storing the old value in a variable block this is possible.

3.8.3 How to use JavaScript

Using JavaScript directly is the most advanced way to add custom logic to your app. It requires you to be familiar with programming and the syntax of JavaScript. Using JavaScript you have full access to all the APIs available in the platform and can create very advanced logic for your app. However if you are not used to writing code it is very easy to make misstakes that will cause the script to malfunction. Therefore it is recomended to use QuickActions and Logic for creating scripts until you feel
comfortable writing code. Javascript API for User Interface Programming

By their nature client-side and server-side scripting are somewhat different. Client-side scripts run in the web-browser or on the mobile-device when the user interacts with your application. You can find API-documentation for client-side scripting over here.


1. Getting the value of a widget

var widget = context.getWidget("MyTextField");
var name = widget.getValue();

2. Setting the value of a widget

var widget = context.getWidget("MyTextField");
var name = widget.setValue("Bill Joy");

3. Submitting a form (including contents of child-forms);

var form = context.getForm("CustomerForm");
   full: true

4. Create a record from a form

Sometimes it is useful to create records from forms without submitting them. Providing the “full”-option will ensure that related records are included, if any, from child-forms and/or widgets holding related data (such as DataGrids etc).

var form = context.getForm("CustomerForm");
var record = form.createRecord({
   full: true

5. Opening another view for showing a record

Case: On the first view you have references to records, perhaps in a DataGrid, and when clicking on a representation of the record (perhaps a row in the DataGrid), you want to open another view with a form holding all the fields of the record.

context.setView("CustomerView", record.id);

6. Publishing a message to a topic

var bus = context.getMessageBus();
bus.publish("#MyTopic", {
   Name: "John Doe",
   Age: 37

7. Listen to messages sent out on a specific topic

var bus = context.getMessageBus();
bus.subscribe("#MyTopic", function(message) {
   context.log("Received a message " + message);

8. Updating an HTML-widget using a query

Case: Sometimes an HTML-widget is used to render data in a custom fashion. The HTML-widgets content is Handlebars-template, so it can reference data. In this example we have an HTML-widget, called MyHTML which needs data from a query as input.

// Get the widget
var html = context.getWidget("MyHTML");
// Get our query-handle
var qh = context.getQueryHandle("MyQuery");

// We just want the first record

// Execute the query
  onSuccess : function(result) {
     var res = result.getRecords();

     // We know we just request a single record
     var rec = res[0];

     // Refresh the html-widget with our record as input-data
}); Using Mobile Plugins

In mobile user-interfaces you can make use of plugins for advanced functionality. Note that first of all you have to add the required plugins to your application. This is done from General -> Mobile Plugins. These plugins are standard Cordova plugins. Appivo has chosen to expose a selection of high quality plugins, if there is a plugin you feel should be included feel free to request it. Each plugin exposes its own API which you can make use of.


var scanner = context.getNativeAPI("barcodeScanner");
scanner.scan(function(result) {
       alert("Scanned " + result.text);
   function(error) {
       alert("Scanner failed " + error);
); Mobile Plugins

The platform supports a number of mobile plugins that provide access to native functionality of the device it runs on – such as geolocation, camera, bluetooth, NFC etc. This section lists these plugins and describe their API and usage.


The Barcodescanner-plugin is a version of the Phonegap Barcodescanner improved by Appivo. You can find the original plugin here  (see the below documentation link for the improved API however).

Sample usage:

var scanner = context.getNativeAPI("Barcodescanner");
scanner.scan(function(result) {
     context.alert("Scanned " + result.text);
  }, function(error) {
     context.alert("Failed to scan: " + error);
     preferFrontCamera: true,
     formats: "QRCODE,PDF_417",
     orientation: "portrait"

See the plugins documentation for further details of the API. Javascript API for Server-side Programming

Rules and Actions are executed on the server-side, this part of the documentation describes the Javascript API for programming ScriptActions.

The Context API

The Context is the main interface when interacting with the system from Actions. Through it you can get access to system-features and general functionality. This section of the document provides a description of its API:

Method Arguments Description
log message (string) Logs a message to the application log at INFO-level
log level (string – TRACE, DEBUG, INFO, WARN, ERROR)
message (string)
Logs a message to the application log at the specified level
getRecord Gets the Record that triggered this Action
getPreviousRecord Gets the previous version of the Record that triggered this Action,
if any
createRecord model (model/string)
data (map/object) attribute-data
Create a record of the given model with the provided values. Does NOT write it to the database.
create record (record/object) Returns the Record again with special system-attributes (such as ID and created_at) populated.
udate record (record/object) Returns the Record again with special system-attributes updated.
get model (string/object) model or name of model
ID (string/ID) ID of record to get
Returns the request Record or null if it does not exist.
delete model (string/object) model or name of model)
ID (string/ID) ID of record to get
Returns a boolean indicating whether the requested Record was deleted or not.
query query (string) query in ASQL or #query-name
params (object/map) query-parameters
Returns a Result
queryForSingle query (string) query in ASQL or #query-name
params (object/map) query-parameters
Returns a Record
queryForArray query (string) query in ASQL or #query-name
params (object/map) query-parameters
Returns an array of Records
publish topic (string) topic to publish message to
message (map/object) message-payload
headers (map/object) optional headers
Publishes a message to the given topic
sendMail options (map/object)
to email-address(es) or role-name(s)
subject subject-line
body plaintext-message
htmlBody HTML-message
Sends an email
sendHttpRequest options (map/object)
method HTTP-method to use (GET, POST, PUT, DELETE), defaults to GET
url URL
headers (map/object – optional) HTTP-headers
parameters (map/object optional) Query-parameters
body (string/object) request-body
onSuccess (function) Success callback
onError (function) Error callback
Sends an HTTP-request to the requested URL and returns an HTTPResponse
fail error (string) An error message Fail this Action with the given error-message. This will rollback any transaction within this context
getRequest Gets the ServiceRequest in scope, if any. This will only be available if the Action was triggered by a REST-Rule.
getResponse Gets the ServiceResponse in scope, if any. This will only be available if the Action was triggered by a REST-Rule.


1. Creating a Record

// Create our Record-object
var record = context.createRecord("MyModel", {
  "Name": "James Gosling",
  "Age": 65

// Write it to the database

2. Execute a Query and iterate over its Result

var result = context.query("MyQuery", {
  "MyParam": 7
try {
  while(result.hasNext()) {
    var record = result.next();
    context.log("Got record with ID: " + record.id);
} finally {

3. Handle a ServiceRequest and interact with an external HTTP-service

// Get the ServiceRequest / ServiceResponse pair
var sreq = context.getRequest();
var sres = context.getResponse();

// Read the body of the ServiceRequest
var options = sreq.readObject();

// Get a value of the JSON-object from the ServiceRequest
var max = options.max;

// Issue an outbound request to a remote service
var response = context.sendHttpRequest({
   "method": "POST",
   "url": "https://foo.bar.com/service",
   "headers": {
      "content-type": "json",
   "body": {
      "threshold": max

// Check the response-status
if (!response.isFailed()) {
  // Read a JSON-object from the response
  var data = response.getContentAsObject();
    "count": data.count
} else {


Method Arguments Description
getValue attribute (string) Name of attribute to get Returns the value of the attribute
getStringValue attribute (string) Name of attribute to get Returns the value of the attribute as a String
getIntValue attribute (string) Name of attribute to get
default (integer – optional) Default value to return in case value is missing or invalid
Returns the value of the attribute as an Integer
getFloatValue attribute (string) Name of attribute to get
default (float – optional) Default value to return in case value is missing or invalid
Returns the value of the attribute as a Float
getBooleanValue attribute (string) Name of attribute to get
default (boolean – optional) Default value to return in case value is missing or invalid
Returns the value of the attribute as a Boolean
setAttributeValue attribute (string) Name of attribute to set
value Value of attribute
Sets the value of the given attribute


Method Arguments Description
hasNext Returns a boolean indicating whether there are more Records to read
next Returns the next Record in the result
close Close this result, no more Records can be read after this.


Method Arguments Description
getResponseCode Returns the HTTP-responsecode
getHeader Header (string) Name of header to get Returns the value of the header as a string, or null if it is not set.
getContentType Returns the content-type of this response
getContentAsObject Returns the response as a JSON-object, if possible
getContentAsXML Returns the response as a DOM-node, if possible
getContentAsString Returns the response as a String, if possible
isFailed Returns a boolean indicating if this is a failure-response


Method Arguments Description
getHeader Header (string) Name of header to get Returns the value of the header as a string, or null if it is not set.
getHeaders Returns all headers as map/object
getParameter parameter (string) Name of parameter to get Returns the value of the requested paramater
readObject Returns the request-body as a map/object (JSON), DOM-node (HTML/XML) or map/object (Form-data)


Method Arguments Description
setStatus response-code (integer) HTTP-responsecode Sets the HTTP-responsecode
setHeader Header (string) Name of header to set
Value (string) Header-value
Sets the value of a header
setContentType content-type MIME Content-type Sets the content-type of this response
setContent content (string) response-body Sets the response-body in string-form. This method and writeObject are mutually exclusive.
writeObject object Object to write as response-body (map/object or DOM-node) Writes an object as the response-body. This method and setContent are mutually exclusive.

3.9 Roles

What are roles?

Roles are used to configure access to your application. Each role can be assigned permissions to use the models of you app. The permissions include Create, Read, Update and Delete operations. The role can then be given to users, giving them the permissions defined by the role.

3.10 User Interfaces

User interfaces are probably the main way that users will interact with your app. You can create as many user interfaces as you want, and control access to them. This means that you could have one interface for administrators, and another interface for standard users. Furthermore, you can also create mobile user interfaces that are intented for mobile phones. Creating a user interface is done through the left-side menu.


After creating a user interface, it will show up in the left-side menu. Selecting it will take you to the user interface editor.

The User Interface Editor

The user interface consists of three tabs:

  • “Views” – Allows you to create, edit and delete views and dialogs. Clicking the settings button (see the image below) brings up a dialog that lets you specify the following settings:

    1. Properties – Lets you edit properties that were set when the user interface was created.
    2. Initial Views – The initial view is first view that is shown to users when they view the app through the user interface. For users who do not have access to the initial view, it is also possible to specify backup views
    3. Rights – Specify privileges required to access the user interface.
  • “Menu” – Here you can define a menu that is shown on top of all views in the user interface. Menu items can be linked to views of your choice.
  • “Theme” – Allows you to define the overarching visual theme of your user interface. A number of precreated themes exist, you can also choose ‘custom’ to define your own theme.



Views are part of a user interface and is the thing that users will see on their phone/computer when interacting with your app. Views typically consists of widgets such as text fields, buttons and images, that display information and enable interaction. Views and their widgets can be styled using CSS, allowing you to design your app to look how you want.

A view may contain one or more forms. Forms can be used by widgets to allow for easy viewing and saving of record data. For more information, see the forms section.

The View Editor

The view editor allows you to define the looks and functionality of a view. On the right side of the view editor is the Widget Palette, from which you can drag widgets into the view. There are a number of different widgets, they are explained more in detail in the [[link: widget section]].




When adding a widget to your view, the widget editor dialog is shown. This dialog allows you to specify the look and behavior of the widget. For example, the widget editor for the button widget lets you specify the text shown on the face of the button, in addition to color and size of the button.

Note: The widget editor dialog is also available by right clicking a widget in the view editor and choosing “edit”.




By clicking the “events” tab in the widget editor you can also specify scripts that should be run when the user clicks the button (see scripts).



3.12 Forms

What is a form?

A form is an easy way to let users display, create and update records through a view. Every form is coupled to a view, a model, and an optional parent form. The model decides which kind of records the form will be able to display/create/update. By specifying preloaded relations, the record loaded by the form will also come with loaded relations. A form can also have a parent form… TODO.

Before any updates are performed, a form must be submitted. Form submission is done using scripts (in fact, submitting forms is so common that there’s even a QuickAction for it). In some circumstances, every field of a form should not be modifyable by users. In these cases, it is possible to specify fixed attributes. For example, you might have a model with a created_by attribute, which should represent the user that created a specific record. By fixing the user relation attribute to the current user, you will get the desired behavior.

Widgets & Forms

Widgets will automatically detect when a form is present on a view and give you an option to couple them to it. In order for the coupled widgets to be able to display any data, the form must first be loaded with a record. This can be done through scripting, but even easier is to specify the record when navigating to the view. This can be done using the ‘Go to view’ QuickAction. When a record is loaded, submitting the form will update that record. Without any record loaded, submission will attempt to create a new record.

3.13 Templates

What are Templates?

Templates are reusable pieces of text that can use variable values to affect their appearance. The variables are accessed using special handlebars syntax. Apart from variables, the template syntax also provides simple flow control.

Template Example

To better illustrate how the templates look and function, here’s an example:

Hello, dear {{record.name}}!

This is a mail from {{app.config.companyName}}, writing to let you know of our recent policy changes.
{{#if param.detail}}
These changes include …………

Best regards,


Template Helpers

Template commands are called helpers and are placed between {{double mustaches}}. There are a number of helpers available:

Helpers available in actions (such as send mail or SMS):

Command Description Example
appIconUrl Returns the URL of the app’s icon {{appIconUrl}}
document Returns the download link of a document record {{document record}}
string Access localization strings. {{string “Hello World”}}
formatNumber Formats a number using dojo number formatting {{formatNumber 10 2}}
getViewURL Returns the URL to the specified view. Optionally provide a record to populate the view’s forms {{getViewURL “view_0” record “myMobileInterface”}}
{{getViewURL “list_data” null “desktop”}}
startsWith True if the specified string starts with the specified value {{startsWith str “value”}}
compare Compares two values. {{compare 10 “==” 5}}
{{compare 10 “>=” record.number}}
arithmeticOperator Performs arithmetic operations {{arithmeticOperator record.number “+” 10
formatDate Formats a date {{formatDate “20161203”}}
documentUrl Creates a URL to a document-record {{documentUrl record}}
file Creates a file link {{file “path”}}
resource Creates a link to a resource {{resource “testResource”}}
concat Concatenates two strings {{concat “string1” “string2”}}
capitalize Capitalizes the first letter of a string {{capitalize “hello”}}
decapitalize Decapitalizes the first letter of a string {{decapitalize “Hello”}}
toUpperCase Converts the entire string to upper case {{toUpperCase “hello”}}
toLowerCase Converts the entire string to lower case {{toLowerCase “HELLO”}}
monthName Gets the month name of the specified date {{monthName “20161203”}}
inlineImage {{inlineImage record}}


Helpers available in views (such as the HTML widget):

Command Description Example
string Access localization strings. {{string “Hello World”}}
formatNumber Formats a number using dojo number formatting {{formatNumber 10 2}}
formatCurrency Formats a number using dojo currency formatting
startsWith True if the specified string starts with the specified value {{startsWith str “value”}}
compare Compares two values. {{compare 10 “==” 5}}
{{compare 10 “>=” record.number}}
arithmeticOperator Performs arithmetic operations {{arithmeticOperator record.number “+” 10
formatDate Formats a date using dojo date formatter {{formatDate “20161203”}}
getWeekDay Returns the weekday name of the specified date {{getWeekDay “20161203”}}
formatTime Formats time {{formatTime “10:25”}}
documentUrl Creates a URL to a document-record {{documentUrl record}}
file Creates a file link {{file “path”}}
resource Creates a link to a resource {{resource “testResource”}}
concat Concatenates two strings {{concat “string1” “string2”}}
capitalize Capitalizes the first letter of a string {{capitalize “hello”}}
decapitalize Decapitalizes the first letter of a string {{decapitalize “Hello”}}
toUpperCase Converts the entire string to upper case {{toUpperCase “hello”}}
toLowerCase Converts the entire string to lower case {{toLowerCase “HELLO”}}
monthName Gets the month name of the specified date {{monthName “20161203”}}
inlineImage {{inlineImage record}}

Several more helpers are available on the handlebars website.

Additionally, for templates used in actions, the following data is available:

Command Description Example
record Access the current record. Use ‘.’ to access attributes or relations {{record.name}}
app Access the current app. From the app you can access models, config parameters, etc. {{app.config.paramName}}

Using the Messagebus

The messagebus allows you to send and receive asynchronous messages between clients and/or application logic residing in the cloud.

4 Internet-of-Things

With Appivo you can not only build web- and mobile-applications, you can also manage and build applications for internet-of-things (IoT) devices. This section of the documentation will cover aspects related to this.

4.1 Provisioning of devices

4.2 Managing devices

4.3 Building IoT Applications

Any application built on the Appivo-platform can have IoT-functionality – meaning you can have devices connected to your application that contribute functionality. Examples of this could be a device monitoring the temperature of a room, controlling a doorlock or displaying information on a TV. An application may even have several kinds of devices connected to it and these different device may need to run different code. That’s why an appivo-application can have several deviceprofiles. A deviceprofile is the collection of code required for a certain type of device when being connected to your application.

The deviceprofile contains a set of event-handlers as the programming model for a device is strictly event-based. The platform emits a few fundamental events – such as App.Start and App.Stop – as you might guess these events are emitted when the application starts and stops, on the device that is. Typically you will initialize datastructures and features when the app starts and then clean everything up when it stops.

Devices can also have timers that emit “tick”-events according to a schedule that you determine, this way you can for instance collect data from a sensor at a regular interval. The device may also “listen” for messages being received on messaging-topics – this allows the device to interact with the part of your application running in the cloud, as well as other devices of course.

Devices may also make use of various plugins, there are plugins for interfacing with hardware components  such as I²C, SPI and GPIO – but that’s not all, there is also a plugin for controlling an embedded chrome-browser which can be used to open up web-based user-interfaces that are also part of your application. This makes it very easy to build a digital signage system that can be used with a regular TV.

4.3.1 The DeviceContext API

The DeviceContext is the main interface when interacting with the device. Through it you can get access to hardware-features and general functionality. Notice that when interacting with the database this API is asynchronous and related methods return a Promise. This section of the document provides a description of the DeviceContext API:

Method Arguments Description
log message (string) Logs a message to the application log at INFO-level
log level (string – TRACE, DEBUG, INFO, WARN, ERROR)
message (string)
Logs a message to the application log at the specified level
setData key (string)
value (anything)
scope (optional string – GLOBAL, APP, PROFILE, LOCAL)
Stores a key/value-pair on the device within the specified scope. GLOBAL means that the key is available in any app running on the device, APP means it is available in any device-profile belonging to this app, PROFILE means it is available only within the current device-profile and LOCAL is the same as PROFILE but not persistent (i.e. will not survive and app-restart or device-reboot).
getData key (string) Gets the value of the given key
clearData key (string) Removes the value for the given key
sleep milliseconds (integer)
nanoseconds (optional integer)
Sleeps for the given amount of time
getAppliance Gets a record that describes the local appliance
getMilliTime Gets the time in ms since 1970-01-01
getModule module (string) Gets a handle to the module with the given name
getUser Gets the current user
createRecord model (model/string)
data (map/object) attribute-data
Create a Record of the given model with the provided values. Does NOT write it to the database.
create record (record/object) Returns a promise which will resolve to newly created Record upon success
udate record Returns a promise which will resolve to the updated Record upon success
get model (string/object) model or name of model
ID (string/ID) ID of record to get
Returns a promise which will resolve to the requested Record
delete model (string/object) model or name of model)
ID (string/ID) ID of record to get
Returns a promise which will resolve to true if record was successfully deleted
query query (string) query in ASQL or #query-name
params (object/map) query-parameters
Returns a promise which will resolve to the queried Records in the form of an array
publish topic (string) topic to publish message to
message (map/object) message-payload
headers (map/object) optional headers
Publishes a message to the given topic


1. Creating a record and storing it in the database.

Note: in this example my SensorValue-model has a relationship to the appliance, so I populate the “appliance_id”-attribute with the appliance-ID.

var sensorData = 7;
var appliance = context.getAppliance();
var record = context.createRecord("SensorValue", {
   "appliance_id": appliance.id,
   "Value": sensorData

2. Getting a record from the database

context.get("Sensor", sensorId).then(function(record) {
   context.log("Got record from database " + record.id);

3. Executing a query

context.query("#MyQuery", {
   "threshold": 100
}).then(function(records) {
   for (var i = 0; i < records.length; i++) {
      var record = records[i];
      context.log("Got record " + i + " with id " + record.id);

4. Publish a message

context.publish("#mytopic", {
   value: 10,
   status: "OPEN"

5. Storing data locally

var counter = compute();
context.setData("counter", counter, "APP");

And retrieving it at a later time:

var counter = context.getData("counter");

4.3.2 The GPIO Module

Method Arguments Description
getPin pinName (string) – A provisioned GPIO pin (e.g. “GPIO 0”) Gets the instance of the GPIO pin
provisionPin options (object) –

    pin: string - GPIO pin name (e.g. "GPIO 0"),
    mode: string - INPUT, OUTPUT, INPUT_OUTPUT
Provisions and returns a GPIO pin, if the pin has already been provisioned, it will just be returned.
setValue pinName (string) – A provisioned GPIO pin (e.g. “GPIO 0”)
value (boolean)
Sets the value of the specified pin (no operation on input pin)
getValue pinName (string) – A provisioned GPIO pin (e.g. “GPIO 0”) Returns a boolean representing the current state of the specified pin


//Make GPIO pin 0 blink 
context.getModule("gpio").getPin("GPIO 0").blink(500); GPIO Pin

Method Arguments Description
setValue value (boolean) Sets the value of the pin (no operator for input pin)
getValue Gets the value of the pin GPIO Input Pin

Inherits all methods from GPIO Pin

Method Arguments Description
isHigh Returns true if the input pin state is high
isLow Returns true if the input pin state is low
addListener listener (function) – receives 1 argument with methods getState and getEdge Adds a state listener to the pin. Returns the pin itself GPIO Output Pin

Inherits all methods from GPIO Pin

Method Arguments Description
blink delay (number)
duration (optional number)
Toggles the pin back and forth between high and low staying in one state for delay ms and running for duration (infinite if not specified)
pulse delay (number) Pulses the pin high for delay ms
pulseLow delay (number) Pulses the pin low for delay ms
addListener listener (function) – receives 1 argument with methods getState and getEdge Adds a state listener to the pin. Returns the pin itself
toggle Toggles the pins state (pin.state = !pin.state)
high Sets the pin high
low Sets the pin low
stop Stops any ongoing pulse or blinking. Is called by all methods except getValue internally to make behaviour predictable GPIO Input/Output Pin

Inherits all methods from GPIO Pin

Method Arguments Description
stop Stops any ongoing pulse or blinking
setModeInput Calls stop and sets the pin to input mode
setModeOutput Calls stop and sets the pin to output mode
getMode Returns the current mode of the pin (“INPUT”, “OUTPUT”)
getValue Calls setModeInput then returns the current state of the pin.
setValue Calls setModeOutput then set the current state of the pin.
isHigh Calls setModeInput then returns if the pin state is high.
isLow Calls setModeInput then returns if the pin state is low.
addListener listener (function) – receives 1 argument with methods getState and getEdge Adds a state listener to the pin. Returns the pin itself
pulse delay (number) Calls setModeOutput then pulses the pin high for delay ms
toggle Calls setModeOutput then toggles the pins state (pin.state = !pin.state)
high Calls setModeOutput then sets the pin high
low Calls setModeOutput then sets the pin low
stop Stops any ongoing pulse or blinking. Is called by all methods except getValue internally to make behaviour predictable

4.3.3 The I2C Module

Method Arguments Description
getDevice bus (number)
address (number)
Gets the i2c device with the specified address on the bus (on raspberry pi the only available bus i 1)


// Get the i2c module
var i2c = context.getModule("i2c");

// Get the device on bus 1, address 0x29
var device = i2c.getDevice(1, 0x29);

// Write 0x30 to the internal register address 0x8A
device.write(0x8A, 0x30); I2C Device

Method Arguments Description
read address (optional number)
size (optional number)
Reads from the internal register(s) on the i2c device. Returns a number if size is not defined and an array if it is.
write address (optional number)
data (number | array<number>)
Write to the internal register(s) on the i2c device.
close Release the device reference

4.3.4 The SPI Module

Method Arguments Description
getDevice cs (number)
channel (number)
Gets the spi device with the specified cs connection on the channel (for most devices channel is not needed so can just be 0)


// Get the SPI-module
var spi = context.getModule("spi");

// Get the device on CS 0
var device = spi.getDevice(0, 0);

//Write 0x0A, 0x0B, 0xC to the device using CS 0 and store the result (2 bytes)
var bytes = device.write([0x0A, 0x0B, 0x0C]);

//assemble the 2 bytes into a single 16-bit unsigned integer (assuming big endian byte order)
var uInt16 = bytes[0] << 8 | bytes[1]; SPI Device

Method Arguments Description
write data (array<number>) Writes bytes to the spi device and returns the response.
write data (string)
charset (string)
Writes a string to the device and returns the response

5 Appivo App Builder Training

Through this training we’ll create a simple issue-tracking system (ie, a HelpDesk app) and in doing so, get some experience with each part of the Appivo App Builder. The lessons are presented in a specific order to introduce key App Builder concepts, but are also written to stand alone as useful reference material. It’s recommended that you start with Lesson 0 and proceed chronologically. Once completed, if you need a reminder or help with a specific concept, just skip to that lesson to refresh your memory.

5.0 Introduction

Note: Appivo works best on Chrome!





That’s how to navigate and get started with the Builder. Proceed to Lesson 1!

5.1 Data Models

Introduction to Data Models

  1. Launch the Appivo App Builder
  2. Give your app a unique name and optional description and save it.
  3. Expand the Logic section on the left and select Models.
  4. Add your first model, called issue. You’ll see the beginning of a model relationship diagram, but it won’t look like much with only one model. 
  5. Switch to the List view and edit the issue model to add attributes.
  6. Add two String and two State attributes. To add attributes, drag the appropriate attribute type from the right-hand menu. 
  7. Rename the first String to summary, then edit it to change the maximum size to 80 characters.
  8. In the same way, change the next String attribute to description, with a maximum size of 6000.
  9. Now change the first State attribute to priority, add some values and select a default.
  10. In the same way, change the next State attribute to status.
  11. Save your app.

Note: You don’t have to add attributes for “created” and “last updated” time stamps. Every model has system-generated attributes called created_at, updated_at, and id that are automatically set and updated when records are created and updated. We’ll see how to use them in later lessons.

Creating Roles

  1. Under the Logic section on the left, select Roles.
  2. Add a new role and change the name from the default MyRole to Supervisor.
  3. Check the Select All box to give this role full access to the issue model.
  4. Save your app.

Note: With Appivo you don’t have to worry about user management or authentication. The Appivo Platform takes care of that for you! We’ll learn more about users in future lessons.

Great work! You’ve taken your first step in building a HelpDesk app.

Proceed to the next lesson to see how things start to come together. Or continue below to learn a little about the Appivo REST API.

The Appivo REST API

Every app has a REST API that is automatically created and maps to its unique data model. The URL pattern is:


The standard REST API operations are available:

  • POST
  • PUT
  • GET
Go Further!

Try out the REST API using the Chrome Postman plugin.







5.2 Views, Forms & Widgets

Create a View and link it to a Data Model via a Form.

  1. Expand the User Interface section on the left and click Create Interface, then click OK.
  2. We’re starting with a blank canvas, so click Create View.
  3. Autogenerated views can be useful, but for this exercise let’s start with a Blank Template with a left menu. Click Next.
  4. We need to give the view a name. We’re creating a form view for issue data, so let’s call it EditIssue.
  5. Under the Widget panel on the right, there are three vertical tabs. Select the Forms tab and click Add Form.
  6. A form is a layer under a view that ties a view to a data model; in this case the issue data model that we created earlier. Call it IssueForm and Save.

Add Widgets to a View

  1. Now back to building the view UI. Select the Widget tab and then click ALL to display all the widgets.
  2. Drag a TextField onto the canvas, connect it to the summary attribute and configure the max length to 80 to match the size of the data model attribute.
  3. Do the same thing with a TextArea widget and the description attribute (max size 6000).
  4. You can grab the edge of the widgets to increase their widths.
  5. Now add two Select widgets and connect them to the status and priority attributes.
  6. You can move them around to organize them as you like.
  7. Add two Button widgets and configure size, color and icons so that you have Cancel and Save buttons.

Configure Events

  1. Double-click the Cancel button and select the Events tab.
  2. Select the Go back action and click OK.
  3. Do the same thing for the Save button, but select the Submit formaction.
  4. Save your app.

Test Drive

Click the Play arrow on the bottom left to launch your app and try it out. This will open a new tab with the run-time form you just created.

Try it out. Enter data, cancel, save… see what happens.

Note: The Cancel button might not do anything because there’s nothing to go back to. Be sure to try it again in a future lesson after we add more views and navigation.

Go Further

Enter the REST API URL into your browser window to view the data.


Eg, https://apps.appivo.com/api/app/SuperHelpfulDesk/model/issue/record

2-01 2-02 2-03 2-04 2-05 2-06 2-07 2-08 2-09 2-10 2-11 2-12 2-13 2-14 2-15 2-16 2-17

5.3 DataGrids & Basic Navigation

Create a list view

  1. Under User Interface on the left, select the web interface that you created.
  2. Add a new view using a Blank template and Left menu template. Name it ListIssue.
  3. From the Widget library, drag over a DataGrid widget.
  4. Since issue is our only data model, it’s already been selected for us. We can also select Enable Sorting.
  5. Now select the Columns tab. You can drag the rows to rearrange the columns, select default system columns to display, and change the display names. Play around with the column settings then click OK.
  6. The default sizing might look squished, but you can resize the DataGrid by dragging the bottom right corner. You can also hide the right-hand menu.
  7. Go back to the UI home and click the Gear icon. From here you can select the ListIssue view to be the initial/default view for the app.
  8. Save your app.

Test Drive

Click the Play arrow on the bottom left to run your app and try it out. If you made the columns sortable, try clicking on the headers.

Go Further

Go back and edit the DataGrid to make status editable. Then save and try it out.

Set up navigation

  1. Edit the DataGrid widget again (by double-clicking it), and select the Events tab.
  2. Add an action to a row double-click event by clicking Addon RowDblClick.
  3. Configure the action as shown, to cause a double-click to open that record in the EditIssue view. OK out of these screens.
  4. Add a New Issue button to the ListIssue view and configure it to open the EditIssue view.
  5. Save your app.

Test Drive

Click the Play arrow on the bottom left to run your app and try it out. Notice that the Back button on the EditIssue view now works because there’s something to go back to!

Go Further

Add another navigation button to the EditIssue view to get back to the ListIssue view.

Note: In the future if you know what your app navigation will look like, you can create a view with navigation buttons to use as a template, and duplicate it to start each additional app view.

3-01 3-02 3-03 3-04 3-05 3-06 3-07 3-08 3-09 3-10 3-11 3-12 3-13 3-14 3-15 3-16

5.4 Rules, Actions & Templates

Create a Rule to trigger an Action

  1. Select Rules under the Logic menu on the left, then click to add a new Rule. Name it NewIssue.
  2. Set the trigger on Data Change, with a condition of after an issue is created.
  3. Click OK.

Create an Action

  1. Move from the Rules tab to the Action tab, and add a new Action called NewIssueEmail.
  2. Enter a manual Sender name but select the Supervisor role for the Email to value. This will cause the rule to send an email to all supervisors (including you!).
  3. Enter your email address manually for the Reply to field.
  4. Enter a message for the Email content.
  5. Go back to your rule and add your action to the Trigger actions section.
  6. Save your app.

Note: Emails will always be sent from <system@appivo.com>, but you can change the sender’s display name and the Reply-to field.


From: SuperHelpfulDesk <system@appivo.com>

Reply to: your-email@email.com

Editor’s Note: Sender display name isn’t currently working.

Test Drive

Click the Play arrow on the bottom left to run your app and try it out. Check your email!

Create a better email

  1. Go back and edit your NewIssueEmail Action.
  2. Edit the email to include references to data from the newly-created issue that triggers the email.
  3. We use Handlebars (handlebars.js) to reference data in templates. In this case, since the Trigger event is a record change (creation of a new issue), We have the context for the new issue record. To reference issue priority and status, simply type:
    1. {{record.priority}}
    2. {{record.status}}
  4. Save your app.

Test Drive

Click the Play arrow on the bottom left to run your app and try it out. Check your email!

 4-1 4-2 4-3 4-4 4-5 4-6

5.5 Relations

Background: A Help Desk ticket usually relates to a specific product but our current application has no way of capturing that detail unless the user includes it in the description or summary… not the best way to capture data! By defining relationships between data models we can capture data in a way that makes it easier to create filters and reports in the future (hint, hint).

Create Relations between DataModels

  1. Select Models under the left-hand Logic menu and add a new model called product.
  2. Double-click the product box to edit it, and add attributes:
    1. name, string (text)
    2. version, float (decimal)
    3. description, string (text)
  3. Adjust the size of the strings and decide how many decimals should be displayed for the version.
  4. Create a relationship between product and issue, by going back to the data model map and grabbing the edge of one model and connecting it to the other. You can rearrange them and adjust the scale to get a better look at it.
  5. Mouse-over the relationship and click the Gear icon to configure the relationship.
  6. Each relationship is two-sided. Click the lines connected to each data model until you’ve established what is shown:
    1. product can have zero-to-many issue
    2. An issue can have zero-or-one product
  7. Save your app.

Note: You can edit Data Models (& Relations) from both the Diagram and List views.

Use Relations in your Views

  1. Go back to the User Interface menu and edit your EditIssue view.
  2. Add a Select widget and notice that it is automatically configured for the product relationship.
  3. Drag the widgets around until they are organized to your liking.
  4. Now edit the ListIssue view.
  5. Double-click the DataGrid and select the Columns tab.
  6. A new column is now available to display the related product data. Enable the column and configure it.
  7. Save your app.

Go Further!

Before you can try your changes, you need to add some product data records. See if you can use what you’ve learned so far to add an EditProduct view, form and necessary navigation.

When you’re done with that, create a ListProduct view by copying the ListIssue view and editing it.

Go Further!

Add a title to your list views by dragging over a Label widget.

Test Drive

Click the Play arrow on the bottom left to run your app and try it out.

Note:  If the products aren’t displaying correctly, open the product data model and select name for the Advanced Label attribute.

Unless a string attribute is present, the default attribute of ID is used, which is a unique alpha-numeric string.

Note: When defining a relation you can choose a non-zero option like one (1) or one-to-many (1…n). In these cases, zero is not an option so there must be a related data record. For example, if every issue has one (1) product, then when running the app and creating an issue, you won’t be able to save/submit unless you’ve also selected a product.

This can sometimes be problematic during development. For example, think about what would happen if you create a number of issues in your app, and then create a new product data model with a one (1) relation. Your app will load the issue data and expect a related product for each one, and get upset (throw errors) when it doesn’t find any. To avoid these errors you have two main options:

  1. Instead of each issue requiring one (1) product, you can define the relation as zero-or-one (0…1). Since zero is acceptable, no rules are broken and your app will be happy.
  2. Start with zero-or-one (0…1), run your app and add a product to each issue, then change the relation to one (1). This will allow you to update your existing issue records, and will ensure that all future issues have a related product.
 5-01 5-02 5-03 5-04 5-05 5-06 5-07 5-08 5-09 5-10 5-11 5-12 5-13

5.6 Filtering Data with Queries

Background: Let’s make the DataGrid on the ListIssues view only show open issues. At the same time, let’s add a menu to filter the DataGrid by product.

Note: There’s quite a lot going on in this lesson, but if you follow the steps you’ll see how everything comes together.

  1. We’re creating a query that we’ll later hook up to the issues DataGrid on the ListIssues view.
  2. We’re configuring that query to only show open issues.
  3. We’re also configuring that query to only show issues for a specified product (by using a parameter).
  4. We’re then defining a parameter that we’ll later hook up to a product menu (Select widget) on the ListIssues view.
  5. We’re creating another query to populate that product menu.

Here we go…

Create Queries

  1. Go to the Queries option under Logic on the left-hand menu, add a new query, and name it OpenIssues.
  2. Move to the Parameters tab and add a new parameter named Product of type ID.
  3. Go back to the Query tab and Select records of model:issue.
  4. Skip Attributes and Pivots, and add Filter Criteria of
    1. status ‘!= value:resolved AND
    2. Product_id = parameter:Product
  5. Save your app.
  6. Add another query called AllProducts.
    1. Select record of model:product
    2. Order by name (ascending)
  7. Save your app.

Use your Queries

  1. Go to the web interface under the User Interface menu on the left-hand menu, and edit your ListIssue view.
  2. Double-click the DataGrid widget to edit it, and select your OpenIssues under the Query parameter.
  3. Add a Select widget to the view and configure it:
    1. Connect To: Query-parameter: OpenIssues/Product
    2. Data Query: AllProducts
  4. Rearrange your view to your liking.
  5. Save your app.

Test Drive

Launch your app and test the filter functionality provided by your new queries. The ListIssue view should only show non-Resolved issues, for the product that is selected.

Note: If your ListIssue view is blank, and you’re sure your queries are correctly configured, make sure you assigned a product to all of your issues, and that your issues aren’t all resolved. The query looks for non-resolved issues related to the selected product.

 6-016-02 6-03 6-04 6-05 6-06 6-07 6-08 6-09 6-10 6-11

5.7 The Navbar Menu

Add a Navigation Bar

  1. Go to the User Interface menu and select your web UI.
  2. Select the Menu tab and add a New Item called Home, linking to the ListIssue view.
  3. Add a New Menu called Issues, and an item under it called New Issue, linking to the EditIssue view.
  4. In the same way, add a new menu called Product with items for New Product  and List Products.
  5. Save your app.

Test Drive

Launch your app and try out your new navbar.

7-1 7-2 7-3 7-4