Functions

With functions, you can express advanced conditions or calculations using JavaScript. The JavaScript code is sandboxed and has read-only access to the data sources defined as parameters.

There are two main use cases for functions:

Function property

Function properties are runtime-calculated values on an Object Class or a Data Source. You may reference other properties on the same Object Class or Data Source. For function values on Data Sources, you may also reference properties in other Data Sources, as well as the Data Sources themselves. Data Sources with cardinality many become arrays, allowing you to do (read-only) array operations, such as iterations.

This screenshot shows the All Users Data Source with a filter including only users missing a value on the Phone property.

The screenshot below shows the function editor accessed from a function property on an object class.

  1. Self Properties: Properties on the same Object Class or Data Source. This section is only available when defining functions on data sources or object classes (e.g., a calculated function property, such as the example below).

  2. App Data: This is all the variables and data sources of your App Data (or Service Data). You may add whole data sources as function parameters (will give you an array of objects as a function parameter), or you may add a data source property or App Variable as a function parameter (will give you a constant as a function parameter).

  3. Code Editor: This is where you write code (JavaScript). You may write as much code as you want, but the code should return some value(s) depending on where you use the function editor. In the below example, we return a string (since the function editor is used for calculating a display property). If the function editor is used to calculate a Visibility Condition, it should return true or false.

  4. The added Function Parameters that may be used in the Code Editor. When adding a data source as a function parameter, it is an array of objects. For example, by adding projects as a function parameter, you may perform array operations such as projects.length, or you may access properties of the data source (for example, the title of the first entry: projects[0].title). The added function parameters are read-only.

  5. Context or Breadcrumb. It helps you keep track of the context of what you are defining a function for. In the example below, we are defining a function for a data source property. If we were defining a function for a visibility condition on a container, the breadcrum (path) to the UI Component would be displayed.

  6. Search your App Data. Makes it easier to locate a property or data source.

  7. Menus for accessing Enumerated Types or JavaScript libraries to add as Function Parameters. You may add all enums from the Global Data Model or the built-in enums (such as Language). You may add built-in JavaScript libraries such as Math.js.

  8. Apply Changes. You may save your function without exiting the editor.

Note that you may use Find and Replace inside the code editor and that the editor has code completion and code suggestions.

Data value

You can use data value functions to return any value type needed. You may include parameters from all available Data Sources, as well as the external libraries.

Examples

Here are some examples of how to use functions to create custom return values.

A welcome message adjusted to the time of the day, using the moment.js library:

const hour = moment().hour()

if (hour >= 23 || hour < 5) 
    return `Good evening, ${firstName}`

if (hour < 12)
    return `Good morning, ${firstName}`

if (hour < 17)
    return `Good afternoon, ${firstName}`

if (hour < 23)
    return `Good evening, ${firstName}`

Concatenate a person's firstName and lastName to a full name value:

return firstName + ' ' + lastName

Check the value of an Enum variable:

if (activeEnvironment === production.value) return 'You are in Production'

Here, activeEnvironment is added from the App Variables, while production is added from the built-in enums from the Enum Constants menu (marked as number 7 in the figure above).

External libraries

The function editor includes a set of external libraries providing advanced functionality.

Math.js

Math.js is a math library. Visit mathjs.org for more information.

Moment.js

Moment.js is a library to parse, validate, manipulate, and display dates and times. Visit momentjs.com for more information.

Here are a few examples (remember to add moment.js as a function parameter first):

// return last day of month (start of day)
return moment().endOf('month'),startOf('day').toJSON()

// Assuming you have a Datetime property myDate added as a function parameter,
// and you want to set the time-part to a 13:00:00.0
let myNewDate = moment(myDate).hour(13).minute(0).second(0).millisecond(0)
return myNewDate.toJSON()

Note that it is necessary to convert the returned moment.js object into a Datetime data type. We do that using the .toJSON() function.

Numeral.js

Numeral.js is a library for formatting and manipulating numbers. Visit numeraljs.com for more information.

Other external JavaScript libraries

Inside Apps, you may use other external JavaScript libraries in addition to these (i.e., client-side javascript libraries). You may add them to the Custom Header Tags section of App Settings. Once added, you will be able to reference that JavaScript inside the Function editor. However, the autocomplete and code validator will not recognize the reference to the external libraries in the editor, but this warning may be ignored.

Advanced functions

Functions can be used to handle advanced logic, like data iteration, creation of complex strings, etc., using JavaScript.

Below are some examples of common use cases for functions and matching JavaScript code. JavaScript ES6 syntax is used in many of the examples. Semicolons are optional.

Handle properties that are null or undefined

By default, all properties in Appfarm (and JavaScript in general) are undefined until a value has been assigned. It may then be blanked (set to null).

The traditional way of handling whether an integer property myProperty is null or undefined is:

if (myProperty !== null && myProperty !== undefined) return myProperty
else return 0

It can be simplified to:

if (myProperty) return myProperty
else return 0

Or, a one-liner:

return (myProperty ? myProperty : 0)

This means: if myProperty has a value, return myProperty, otherwise return 0.

Create concatenated strings of properties

You want to create a string containing static text combined with property values. For example, to generate a dynamic welcome message, interpolating the values of the function parameters firstName and companyName.

return `
Welcome, ${firstName}!

You are logged in on behalf of the company ${companyName}.
`

This example uses backticks (`) at the start and the end. Inside the backticks, you can substitute strings using placeholders (${propertyName}). The result is a merged string with line shifts preserved. This concept is known as template literals in JavaScript.

Check if a filtered data source contains any objects

You have a data source Companies containing all companies, and you want to create a condition that evaluates to true if there are any active companies in this data source. A function parametercompanies is added, with a filter state EQUALS active:

if (companies.length > 0) return true
else return false

Or, as a one-liner:

return companies.length > 0

Return a single value from an array

You have a data source Companies with cardinality many. Companies are added as a function parameter, with a filter applied, resulting in this array containing 1 or 0 objects. You want to return the property Company Name if the filter resulted in an object, and null otherwise:

if (companies.length > 0) return companies[0].companyName
else return null

Or, as a one-liner:

return companies.length > 0 ? companies[0].companyName : null

Good to know

When adding whole data sources as function parameters, they are treated as arrays inside the function editor, as in the above example. However, you cannot to refer properties more than one level deep. In other words, if you add Contacts as a function parameter, you cannot access contacts[0].company.name.

In this case, you need to add a runtime property (function) to the Contacts data source (for example companyName, returning Contacts.Company.Name) and refer to this property as Contacts[0].companyName.

Return a reference to an object

You have a data source Order Lines and need a property Company on this data source. However, Company is located at Order Lines.Order.Company.

The solution is to create a runtime property on the Order Lines data source, with data type Company, and property type Function. Add Order Lines.Order.Company.ID as a function parameter:

return id

Note that if you add an array as a function parameter, and want to access the built-in ID property of an object, you need to use Node Name, which is _id.

Aggregate / accumulate data from an array

You have added salesLines as a function parameter, an array containing all relevant sales to be accumulated. You want to sum the Amount property of the sales. When referring to properties of an array added as a function parameter, you need to use the Node Name (as defined in the object class properties) of this property. In our case, the Node Name of Amount is amount:

return salesLines.reduce((agg,obj) => (agg + obj.amount), 0)

If amount could potentially be undefined or null, we could handle that as follows:

return salesLines.reduce((agg,obj) => (agg + (obj.amount ? obj.amount : 0)), 0)

Filter an array inside the function

You have added salesLines as a function parameter, an array containing all sales. You want to filter only those with a positive amount, and save this array to a new array salesLinedFiltered.

// Loop all instances, and keep only those matching the condition amount >= 0
let salesLinesFiltered = salesLines.filter(obj => obj.amount >= 0)

Note that in this example, you would typically just apply a filter to the function parameter salesLines (with the expression amount >= 0). This code sample is just to demonstrate the filter method of JavaScript arrays.

Convert an array of objects into an array of simple data types

You have a Companies data source, with a property Company Name (Node Name: companyName). You want to return a comma-separated list of all company names. For this, we need to convert the array of objects to an array of strings, and then join the strings with a comma.

// Map all Company objects into an array of strings, and then join the strings
let companyStrings = companies.map(obj => obj.companyName)
return companyStrings.join(',')

Convert an array of objects into an array of alternative objects

You have a Companies data source, with the properties companyName and organizationNumber. You would like to convert this to a list of objects with the properties name and orgNo. This might be used in a service (as the custom response body of an endpoint) when you want to return a list of objects with property names according to some specific cases, or with only a small set of properties from the Companies data source.

/*
'companies' is added as a function parameter.
An array is returned since this is common when returning a custom response body from a service endpoint.
*/ 
let newCompanyList = companies.map(comp => ({name: comp.companyName, orgNo: comp.organizationNumber}))
return newCompanyList

// If you need to return a string: return JSON.stringify(newCompanyList)

Create a nested JSON structure from two data sources

You have an endpoint that returns data to an external system. There are two data sources, Orders and Order Lines, and you would like to return a list of orders and, for each order, a list of related order lines.

/*
'orders' and 'orderLines' have been added as function parameters.
'orderLines' holds the order lines for all of the orders.
*/
return orders.map(order => ({
    _id: order._id,
    title: order.title,
    customer: order.customerName,
    lines: (orderLines.filter(line => line.orderID === order._id).map(line => ({
        _id: line._id,
        product: line.productName,
        qty: line.quantity,
        price: line.price
    })))
}))

Sort an array of objects

You have a data source Orders and want to return the Order Date (Node Name: orderDate) of the first order.

let ordersSorted = orders.sort((a,b) => (a.orderDate > b.orderDate) ? 1 : -1)
return ordersSorted[0].orderDate

Note that in this example you could just use the Sort objects action node on the Orders data source (sorted Ascending by Order Date) prior to adding it as a function parameter, and having a function return orders[0].orderDate.

Last updated