Introduction
One of the great things about Appsmith is the ability to write JavaScript just about anywhere! Any input, text label, color, visible or disabled toggle, or any other setting can be made dynamic with a JavaScript expression. This allows you to programmatically change the value with variables like the user's email, a value from a table row, or any other input you want to use.
There are lots of places in Appsmith to write JavaScript, and there are lots of formats or syntax to use in different places! This can be a little confusing at first, especially if you're new to JavaScript. In this guide, I'll break down the different places where JavaScript can be used, and how the code formatting differs when writing it in JSObjects vs widget properties.
First, we'll cover some basics about JavaScript functions in general, before we get into the Appsmith specifics. Then we'll see how this applies to the various places you can write code in Appsmith, and follow it up with a few practical examples. Let's get started!
Functions vs Expressions vs IFFEs
JavaScript in Appsmith can be written in various forms, including functions, expressions, and Immediately Invoked Function Expressions (IIFEs). Understanding these different formats is essential for utilizing JavaScript effectively in your Appsmith applications.
Functions
Functions are reusable blocks of code that can be defined and called multiple times. They are useful for encapsulating logic that needs to be executed in various parts of your application. Functions can be used to modify variables, trigger other functions, or return a value. Note, not all functions return a value though.
-
Function Declaration:
function add(a, b) { return a + b; } console.log(add(2, 3)); // 5
-
Function Expression:
const add = function(a, b) { return a + b; }; console.log(add(2, 3)); // 5
-
Arrow Function:
const add = (a, b) => a + b; console.log(add(2, 3)); // 5
Expressions
Expressions are snippets of code that evaluate to a value. In Appsmith, JavaScript expressions can be used within {{ }}
to make widget properties dynamic. Valid expressions always return a value, even if that value is null or undefined.
-
Example:
// Setting the text property of a text widget {{ appsmith.user.email }}
Here,
{{ appsmith.user.email }}
is a JavaScript expression that evaluates to the user's email.
IIFEs (Immediately Invoked Function Expressions)
IIFEs are functions that are defined and immediately executed. They are useful for creating a local scope to avoid polluting the global namespace and for executing code right away.
-
Syntax:
(function() { console.log("This is an IIFE"); })();
This function is defined and immediately invoked, printing "This is an IIFE" to the console. Unlike regular functions, simply defining it actually runs the function, so there's no need for an extra line to call the function after defining it.
Note the two extra sets of parentheses. First the function is wrapped in parentheses to keep it from being declared normally with the other functions and variables. Then, it's followed by a set of parentheses to call the function. So
myFunction(){}
becomes(myFunction(){})()
. -
Arrow Function IIFE:
(() => { console.log("This is an arrow function IIFE"); })();
Use Cases in Appsmith
- Functions: Use for reusable logic that needs to be called multiple times within your application. Define functions within JSObjects for complex logic and interactions.
- Expressions: Use for simple dynamic bindings directly within widget properties. Ideal for setting text, styles, visibility, and other properties based on application state or user input.
- IIFEs: Use for executing initialization code or setting up scoped variables that should not interfere with other parts of your application.
Regular Functions vs Arrow Functions
JavaScript introduced arrow functions ()=>{}
in 2015 with the release of ECMAScript 6 (ES6). Prior to that there was just one format for writing function, but now when you hear the term regular functions it's referring to the original, non-arrow format.
Regular Function Syntax
-
Without Parameters
function greet() { return "Hello, world!"; }
-
With Parameters
function add(a, b) { return a + b; }
Arrow Function Syntax
-
Without Parameters
const greet = () => { return "Hello, world!"; }
-
With Parameters
const add = (a, b) => { return a + b; }
- Simplified Arrow Function with Implicit Return (Single-Line)
-
const greet = () => "Hello, world!"; const add = (a, b) => a + b;
- In the case of single-line arrow functions, the opening and closing brackets can be omitted.
Arrow functions that return an object
- Multi-Line Arrow Function Returning an Object
-
const createUser = (name, age) => { return { name: name, age: age }; };
- Single-Line Arrow Function Returning an Object
-
const getUser = () => ({ name: "John", age: 30 });
When returning an object, the opening and closing curly braces of the object would get interpreted by JavaScript as the opening and closing of a function. Wrapping the object in parentheses tells JavaScript to return an object directly, instead of evaluating the {}
contents as a function. This saves a few characters by not having to type return {...}
.
- Single Parameter (Parentheses Omitted)
-
const square = x => x * x;
NOTE: The parenthesis are only optional when there is one parameter. Zero params must have the empty ()
as a placeholder, and more than one parameter must be wrapped in ()
.
Behavioral Differences
this
Binding
The this
keyword in JavaScript refers to the context where the function is running, and provides access to the variables and other functions defined there. This environment of surrounding code is known as scope, and the scope of this
is different for arrow functions and regular functions in the same environment.
- Regular Functions
this
refers to the object that called the function.-
Example:
const obj = { value: 42, regularFunction: function() { console.log(this.value); // 42 } };
The console.log inside the
obj
variable can "see" the value (42) even though it's outside the function.
- Arrow Functions
this
is lexically bound, meaning it usesthis
from the surrounding code context inside the function where it is called, and can't see outside the function.-
Example:
const obj = { value: 42, arrowFunction: () => { console.log(this.value); // undefined, as `this` is not bound to `obj` } };
In this case, the value is undefined because the arrow function's
this
can only see inside the same function.
If all this talk about this this
thing is confusing, thank JavaScript! This is a regular JS thing, not Appsmith specific, but it will come into play later so keep it in mind.
Arguments Object
- Regular Functions
-
Have access to the
arguments
object, which is an array-like object containing the arguments passed to the function. This is just a different way of accessing the parameters that were passed to the function, so they can be selected by index instead of by the variable name.function regularFunction(a, b, c) { console.log(arguments); // Logs: [Arguments] { '0': 1, '1': 2, '2': 3 } } regularFunction(1, 2, 3);
-
- Arrow Functions
- Do not have their own
arguments
object. They use thearguments
object from the nearest non-arrow function. -
Example:
const arrowFunction = (a, b, c) => { console.log(arguments); // ReferenceError: arguments is not defined } arrowFunction(1, 2, 3);
- Do not have their own
Using JavaScript In Appsmith
JavaScript can be used in many forms in Appsmith, from expressions, to functions, JS Objects, and Immediately Invoked Functional Expressions (IIFEs).
Let's start with the most basic example: a text widget displaying the user's email.
Here you can see the text widget has a portion of hard-coded text saying Hello
, and a dynamic portion that references the user's email from the appsmith object. The widget's visibility is also being dynamically controlled based on the email, so it would be hidden in a public app. The opening and closing {{}}
allow you to write JavaScript.
In this case, the JS is an expression, not a function. It's just a reference to other values in the current scope, that resolves to a string (user email) or a boolean value (true if email exists).
JavaScript can also be used to filter data in the table widget properties.
This works ok for simple filters, but usually it's better to use a JSObject to write the code in a full JS editor instead of directly in the widget property.
Declaring Variables and Functions in a JS File vs a JSObject in Appsmith
When you need more room to write code outside of a widget property, JSObjects provide a full JavaScript IDE that can access all the widgets, APIs and database queries in your app. You can declare variables and functions that can be referenced anywhere else in your app. JSObjects wrap the code in an export default {}
statement, and all of the containing code must be a valid object, with key:value pairs separated by commas.
So instead of writing:
const myVar = 42;
function logVar(){
console.log(myVar);
}
The code inside a JSObject would be written as:
export default {
myVar: 42,
logVar(){
console.log(myVar)
}
}
Key differences
- Inside an JSObject, there's no concept of
var
,let
, orconst
when declaring the top level properties. So the type of variable or constant can be omitted. - Again, since this is inside an JSObject, key=value pairs are defined with a
:
colon instead of an=
assignment operator. - Since each function or variable is being defined in an JSObject, they are separated with commas instead of semi-colons like in a JS file.
- The
function
keyword is omitted when defining a named function.
More on var, let & const here.
And here's an example of filtering data regular function with an arrow function inside the filter.
Arrow Functions in a JSObject
Named arrow functions can be defined in a JSObject using syntax similar to defining other variables.
export default {
myVar: 42,
logVar: () => {
console.log(myVar);
}
};
However, since arrow functions have their own this
object, they don't have the same access to the rest of the code in your JSObject. As a best practice, only regular functions should be used at the top level key:value pairs in the JSObject. But it's ok to use arrow functions inside those regular functions.
Conclusion
Understanding the differences between regular and arrow functions, their syntax, and behavioral traits is crucial for writing efficient JavaScript in Appsmith. Here's a recap of the key points covered in this guide:
- Regular vs Arrow Functions: Arrow functions introduced in ES6 provide a shorter syntax and lexical
this
binding, making them useful for callbacks and functional programming patterns. - Single-Line vs Multi-Line Functions: Arrow functions can have implicit returns when written in a single line, with the ability to omit parentheses for single parameters.
- Returning Objects in Arrow Functions: Wrapping objects in parentheses is necessary to avoid syntax errors.
- Scope and
this
: Regular functions use dynamicthis
binding, while arrow functions use lexicalthis
, which affects how they access surrounding context. - Arguments Object: Regular functions have their own
arguments
object, whereas arrow functions do not. - Using JavaScript in Appsmith: JavaScript can be embedded in widget properties, expressions, and JSObjects, allowing for dynamic, programmable applications.
- JSObjects in Appsmith: JSObjects provide a robust environment to write and manage JavaScript code with full access to widgets, APIs, and database queries.
By mastering these concepts, you'll be better equipped to utilize the full power of JavaScript within Appsmith, creating more dynamic and responsive applications. Happy coding!