Coded component

Coded Component enables you to create custom, interactive UI components while still retaining access to your data model. A component can optionally leverage external libraries.

Coded Component is an advanced feature that requires knowledge of JavaScript. It should be considered as a fallback option when a particular component cannot be satisfactorily built using the standard components within Appfarm Create.

Typical use cases include implementing interactive components such as maps, audio/video playback, and 3D viewers, or integrating third-party widgets where using an Iframe is undesirable.

Coded Component examples are available in our Showroom. You may view the demos and access the setup in Appfarm Create. If you do not have access, you may register here.

Properties of the Coded Component

Element ID

A unique ID for the DOM element containing the coded component. This ID can also be used to link directly to the element on the page.

HTML Content

HTML that will be included inside the coded component HTML element. You should not include DOCTYPE declarations and headers, as these are automatically handled by Create and might create conflicts. Content in script tags will not be run. If you have scripts, these should be placed in the Script section, whereas links to external scripts should be placed as Script URL in the Resources section.

All the HTML in the coded component is placed in a div when running in your app. Therefore, DOCTYPE declarations and headers might conflict with other elements in your app.

Script

JavaScript to be executed by the coded component. You can access exposed data, actions, and DOM elements within your script through the defined namespace. <script> tags should be omitted.

You cannot use import statements in your JavaScript code. Instead, external libraries should be added as external resources to the coded component. Most packages will add functions to the global scope, and these can be accessed inside the script section. However, you might get a warning that the package you’re using is undefined. This is a false positive and can be ignored. If you’re adding multiple packages with dependencies on each other, make sure that they are being loaded in the correct order.

To access DOM elements, use <namespace>.element. This will let you access the HTML elements that you have defined in the HTML Content through regular JavaScript. You do not need to wait for the DOM to load before executing your script; Appfarm will automatically handle the timing of the DOM and Script.

<canvas style="width: 100%; height: 100%;"></canvas> And the following javascript to the Script section.

const { data: { level, play }, element } = appfarm

const canvas = element.querySelector('canvas')

const src = 'https://storage.googleapis.com/dev-appfarm-public/' 
const stateMachineName = 'State Machine'

const r = new rive.Rive({ // Create will throw a false positive about Rive being undefined 
    canvas, 
    src, 
    autoplay: true, 
    stateMachines: stateMachineName, 
    layout: new rive.Layout({ 
        fit: rive.Fit.Cover, 
        alignment: rive.Alignment.Center, 
    }), 
    onload: (...args) => {
        const inputs = r.stateMachineInputs(stateMachineName);
        
        play.on('change', (val) => {
            if (val) {
                r.play()
            } else {
                r.pause()
            }
        })
        
        level.on('change', (val) => {
            inputs[0].value = val
        })
        
        inputs[0].value = level.get()
    },
});

Calling Actions

You can trigger actions from within the script of a coded component, and optionally pass in action parameters. See Actions below for information on how to configure what actions you want to be available from within the script.

Within the Script property of the coded component, the connected actions are made available within an actions object in the specified namespace. You may invoke an action with the syntax <namespace>.actions.<action_name>().

Invoking an action returns a Promise, which will be resolved when the action is finished.

appfarm.actions.sendNotification()
    .then(() => console.log('done!'))
    .catch(console.error)

To execute actions in sequence safely, see the MDN guide on Promise composition.

For actions with parameters, specify an object as a function parameter when you invoke the action. This object should contain key/value pairs where the key is the name of the code function param and the value is the value you wish to pass to the action.

// Executing action "Update Animal" with 2 action params from a Script
appfarm.actions.updateAnimal({
    id: '6262a41f6bf4df7b3d7bfe02',
    age: 10
});

Integrating with data

You can access your solution's data model within the script of a coded component by connecting data sources and individual values. See the Data section below for information on how to configure the connection.

Within the Script property of the coded component, the connected data items are made available within an data object in the specified namespace. Methods are provided to get and set values, as well as react to value changes.

Data Source

Both database-connected and runtime data sources can be connected to a coded component.

A single-cardinality data source will be returned as an object, while a many-cardinality data source will be returned as an array of objects

No method is provided to update objects in data sources. To do that, you should connect an action, using action params to pass values if required.

.get()

Return the object(s).

let species = appfarm.data.animals.get();

.on('change', handler)

Single-cardinality data source:

appfarm.data.tree.on('change', (data) => {  
    console.log(data);
    // {"_id":"6290c8bd4cd17a5bee0bf08b", commonName: "London plane"}
})  

Many-cardinality data source:

appfarm.data.animals.on('change', (data) => {  
    console.log(data);
    // [{"_id":"6262a41f6bf4df7b3d7bfe02", name: "Terrence", ...}, {...}, {...}]
})  

Value

A value can be any individual property from your data model, such as an app variable, resource file, or data source property.

The examples below are for a value of type String with the name species within the default namespace appfarm.

.get()

Return the current value.

let species = appfarm.data.species.get();

.set(value)

Update the value.

appfarm.data.species.set('Enteroctopus dofleini');

.on('change', handler)

React to a change in the value. The change can occur within the script, or elsewhere in your solution.

appfarm.data.species.on('change', (val) => {
    console.log(val);
    // Enteroctopus dofleini
})

Resources

External JavaScript and CSS resources that you want to make available to the coded component. These resources can be embedded (Script, Stylesheet) or linked (Script URL, Stylesheet URL). Custom attributes can be added per resource, and these are included in the script/link tag that is generated. Please make sure that all Script URLs and Stylesheet URLs are accessible, as they can lead to failure to execute the script if they are not available.

Follow the instructions under Content security to ensure the client can load any externally hosted resources.

Best practice

CSS added within the Coded Component is applied to the entire document, including the Developer tools. To avoid unexpected changes to other components, you should prefix your selectors with an ID selector according to the Coded Component's Element ID.

Namespace

The name of the namespace exposed to the script. Defaults to "appfarm".

The namespace exposes:

  • The DOM element: appfarm.element

  • Data: appfarm.data

  • Actions: appfarm.actions

Additionally, an event will be emitted on the namespace when the coded component is unloaded.

appfarm.on('unload', () => {  
    // Perform clean-up
})  

Data

This is where you add Data Sources to be used inside the Script of your Coded Component. You may add Data Sources or Values (e.g. a property of an object in a data source, or an App Variable). For how to use the data sources and values added in the Data section from within a script, see the Integrating with data section above.

Name

A unique name for the data item. Required.

Value Type

The type of value for this data item. Either Data Source , Value or Read-Only Value .

Select Data Source to make an entire data source available, either as an object (single cardinality) or as an array of objects (many cardinality).

Select Value to make a distinct value available. Read-Only Value is similar to Value, but as the name states, it may only be read - not modified from within the script.

Data Source/Value

The data source or value to expose.

Selection

Apply a selection if you have chosen a many cardinality data source above.

  • All objects (default)

  • Selected object(s)

  • Object in context

  • Filtered selection

Description

A description of the data for internal reference.

Here are some examples of how to retrieve and listen to the data available to your coded component.

// Listen to a change in the value
appfarm.data.value.on("change", (val) => {
    // Do something with the newly changed value here
})

// Retrieve the value of a readOnlyValue
appfarm.data.readOnlyValue.get()

// Get all elements that are selected in the data source
appfarm.data.dataSource.get().find(el => el.__SELECTED__ === true); 

Note that you should not use a coded component to write to a data source. For this purpose, you should use an Action with action params to handle the updating of any objects.

Actions

Actions in your app that should be exposed to the script via a function. For how to trigger the Actions added in the Action section from within a script, see the Calling Actions section above.

Function Name

A unique name for the function. Required.

Action

The action to expose. If the action has parameters, these can be passed in by the script by specifying the parameter as a Code Function Param. The code function param will get a default name, but this can be edited.

Content security

By default, Appfarm implements strict content security settings to protect users against common attacks. If you are referencing resources hosted on external domains, you will need to specify those domains within Script Sources or Style Sources under Content Security within a given environment configuration.

Depending on how your component is implemented, you may also need to specify domains under Web Request Targets and Image Sources. For example, use of the Mapbox GL JS library will require that their https://api.mapbox.com domain is whitelisted for both of these content types to load a map successfully.

Last updated