Create JS Objects
The JavaScript Editor in Appsmith enables you to create JS Objects with a page-level scope. A JS Object is an encapsulation of variables and functions associated with it. It is a template similar to a Java class that contains the variables and the methods used to perform actions.
JS Objects are accessible only within the page where they're defined and aren't accessible across pages.
Create JS Objects
You can create JS Objects from the entity explorer on the left sidebar.
Click the Explorer tab. Click + next to Queries/JS. Select New JS Object from the list of options.
A JS Object has the following structure:
export default {
myVar1: [], // define array
myVar2: {}, // define object
// define functions
myFun1: () => {
//write code to manipulate the data or perform actions like executing a query
},
myFun2: async () => {
//use async-await or promises
// this helps when you want to trigger queries at run time.
// For example, API_NAME.run() - run method executes the call and returns the response.
}
}
The JS Object notation should start with export default
. The export default declaration exposes functions that are part of the JS Object to the page where it is defined.
You can access the properties of the JS Objects defined using the dot or bracket notation.
Example:
export default {
useJSObjectProps: () => {
const userObject = new Object(); //object with method scope and is local to this method.
userObject.id = 1; // setting property using dot notation
userObject[“name”] = “John”; //setting property using bracket notation
showAlert(userObject.id);
showAlert(userObject[“name”]);
}
}
Execute functions
Click the Run button on the top right corner to execute the JS function. If your JS object has only one function defined, the editor defaults the function name. But if your JS Object has more than one function defined, you can select the function you want to execute and then click Run. If your code has errors, the Run button is greyed out and blocks the execution until the errors are resolved. You can also execute the function using a shortcut key CMD+ENTER or CTRL + ENTER
Use variables for state management
JS Object variables are mutable and can be used to contain the reference to a value. You can use it to store your data in a non-persistent state. As the storage state is non-persistent, the data is saved until the page reloads or until the user closes the window. JSObject variables can be of any data type, such as Number, String, Object, Array, Date object, etc.
Example 1: a counter that increments or decrements the value in a JS Object variable.
Create a JS Object
JSObject1
and add the following code:export default {
counter: 1,
increment: () ⇒ {
this.counter = this.counter + 1
},
decrement: () ⇒ {
this.counter = this.counter - 1
}
}Drop a Text widget on the canvas and display the value of the counter variable in the Text property as shown below:
{
{
JSObject1.counter;
}
}Drop two Button widgets labelled Increment and Decrement and add the following code in onClick event to trigger these functions:
//for increment
{
{
JSObject1.increment();
}
}
//for decrement
{
{
JSObject1.decrement();
}
}Test the buttons and notice the value change in the Text widget.
Example 2: mutating values in an array object
export default {
users: [{name: "a"}, {name: "b"}, {name: "c"}, {name: "d"}],
modifyFirstUserName: () ⇒ {
this.users[0].name = "Hello"
}
}
Variable mutations can be triggered only using event listeners(async fields). For example, you can't update the variable value using the Text property on the Text widget, but you can use the onClick event on the Button widget.
Write complex code
When you build applications, it’s often more than just a CRUD operation from a single datasource. You might integrate with multiple APIs or want the dataset created by querying multiple tables to traverse data, filter, or manipulate the response by calling different APIs. You can write complex logic with ease using JavaScript Editor.
Use case
You want to create a developer task tracker dashboard, update developer information, and add some permissions, like ensuring only admins can access the dashboard.
Code workflow
Let’s create a task tracker dashboard that gives you an overview of task progress. You have a query called “developers,” which fetches the developers’ information like name, email, etc. You have an API, getAllDeveloperTaskStatus
, that gives you a task list with the status for each developer.
Task Tracker
You want to display the developer details in a tabular format so that you can choose a table widget. You would want to show a total number of tasks like total tasks, no of completed tasks, no of to-do tasks, and no of in-progress tasks. You can achieve this by adding a JS Object and creating one function in it, “getTaskList.” Let’s write the code for this function using the JavaScript Editor.
- In the Explorer tab, >> click (+) next to
JS Objects
>> SelectNew JS Object
. - It’ll open a JS Editor. Rename the JS Object to
Utils
- Add the below code snippet in the Code tab.
export default {
getAllDeveloperTaskStatus: () => {
return developers.data.map((developer) => {
const developerId = developer.id;
const taskList = developerTaskStatus.data.filter((alltask) => alltask.userId === String(developerId)); // fetch all the tasks for the given developer
const completedTaskList = taskList.filter((task) => task.status === "Completed"); // fetch tasks that are completed by the developer
const todoTaskList = taskList.filter((task) => task.status === "To-Do"); // fetch tasks that aren't yet started by the developer
const inProgressTaskList = taskList.filter((task) => task.completed === "In-Progress"); // fetch tasks that are in progress by the developer
return {
"Id": developer.id,
"Name": developer.name,
"Email": developer.email,
"Total Tasks": taskList.length,
"To-Do Tasks": todoTaskList.length,
"In Progress Tasks": inProgressTaskList.length,
"Completed Tasks": completedTaskList.length
};
})
}
}
The getAllDeveloperTaskStatus
function does the following:
- Reads all the developers’ data
- Iterates for each developer record
- Scans the
tasklist
to fetch the developer-related tasks- Based on task status (TO-DO, In-Progress, and Completed)
- Generate a response that gives a cumulative task breakup for each task status for each developer
- Scans the
To display the response generated bygetAllDeveloperTaskStatus
bind it to a widget.
Navigate to
Page
>> SelectTable
widget in Widgets Tab >> Drag it onto the canvas.Select the
Table
Widget on the canvasIn the
Property Pane
on the right side add the code{{Utils.getAllDeveloperTaskStatus()}}
in theTable Data
property.
The table widget shows the data in the screenshot below.
Update Developer Information
Let’s add another function where you can select a developer record and update the information.
To achieve this, add a new column to the table widget and select the Column Type
as an icon button
type that opens a modal window.
Now that the Edit
column appears on the table, let’s add a modal to fetch the developer
information like Name
and Email
.
Navigate to Explorer >> Select Utils
>> Add the code snippet for updateUserData()
after getAllDeveloperTaskStatus()
.
export default {
getAllDeveloperTaskStatus: () => {
...// code block for getAllDeveloperTaskStatus
},
updateUserData: () => {
// Construct new user's data
const newUserData = {
...Table1.selectedRow,
name: devName.text,
email: devEmail.text,
}
// Run updateDeveloperDetails query
updateDeveloperDetails.run(
// on successful execution of updateDeveloperDetails run developers to fetch updated data
async () => {
await developers.run();
closeModal("editModal");
showAlert("Developer Details are updated successfully!");
},
// On Error, close the modal and show an error toast
(e) => {
closeModal("editModal")
showAlert("An error occurred while updating the developer details!");
if (e.statusCode === 401) {
navigateTo("Page1");
}
},
// Params Object
newUserData)
}
}
The updateUserData
function does the following:
- Create the dynamic data that are updated by the user in
editModal
innewUserData
JSON - Executes query
updateDeveloperDetails
- On successful execution of the query:
- Calls
developers
query to fetch the updated developer details - Shows a success toast to the user
- Closes the
editModal
- Calls
- On error or failed execution of the query
- Shows an error toast to the user
- Closes the
editModal
- On successful execution of the query:
Rename the Confirm
button to Update
and bind the function updateUserData
to its onClick()
event. You can bind the function call by using {{Utils.updateUserData()}}
.
When you hit the Update
button, the updateUserData
function gets executed which updates the developer information and refreshes the developer table to fetch the updated information.
Add Permissions
As you want only the admins to access the dashboard, let’s create another function for access control. The function contains a list of users’ emails who can update data in the table. Let’s call this function isAdmin
.
Add the code snippet for isAdmin()
and adminsList
to the Utils
JS Object.
export default {
adminsList: ["admin1@yourdomain.com", "admin2@yourdomain.com", "admin3@yourdomain.com"],
getAllDeveloperTaskStatus() => {
... // code block for getAllDeveloperTaskStatus
},
updateUsersData() => {
... // code block for updateUserData
},
isAdmin: (email) => {
if (this.adminList.indexOf(appsmith.user.email) > -1) { // check if the logged in user is an admin
return true;
}
return false;
}
}
The final function looks like the below:
export default {
//the allowed list of admin's email
adminsList: ["admin1@yourdomain.com", "admin2@yourdomain.com", "admin3@yourdomain.com"],
getAllDeveloperTaskStatus: () => {
return developers.data.map((developer) => {
const developerId = developer.id;
const taskList = developerTaskStatus.data.filter((alltask) => alltask.userId === String(developerId)); // fetch all the tasks for the given developer
const completedTaskList = taskList.filter((task) => task.status === "Completed"); // fetch tasks that are completed by the developer
const todoTaskList = taskList.filter((task) => task.status === "To-Do"); // fetch tasks that aren't yet started by the developer
const inProgressTaskList = taskList.filter((task) => task.completed === "In-Progress"); // fetch tasks that are in progress by the developer
return {
"Id": developer.id,
"Name": developer.name,
"Email": developer.email,
"Total Tasks": taskList.length,
"To-Do Tasks": todoTaskList.length,
"In Progress Tasks": inProgressTaskList.length,
"Completed Tasks": completedTaskList.length
};
})
},
updateUserData: () => {
// Construct new user's data
const newUserData = {
...Table1.selectedRow,
name: devName.text,
email: devEmail.text,
}
// Run updateDeveloperDetails query
updateDeveloperDetails.run(
// on successful execution of updateDeveloperDetails run developers to fetch updated data
async () => {
await developers.run();
closeModal("editModal");
showAlert("Developer Details are updated successfully!");
},
// On Error, close the modal and show an error toast
(e) => {
closeModal("editModal")
showAlert("An error occurred while updating the developer details!");
if (e.statusCode === 401) {
navigateTo("Page1");
}
},
// Params Object
newUserData)
},
isAdmin: async () => {
if (this.adminsList.indexOf(appsmith.user.email) > -1) {
return true;
}
navigateTo("Page2");
return false;
}
}
Only the users with email added to the adminList
can access the dashboard and do the updates.
With the Async function settings, you can bind the isAdmin
function to RUN ON PAGE LOAD
. The execution of IsAdmin
on page load ensures the validation of the user’s email against the adminList
for the logged-in user should happen on the page load. If the logged-in user's email is in the adminsList
, the user can access the dashboard. If not, the user navigates to the denied access page with the message 'You don't have permission to access the Dashboard'.
Current limitations
- You can't use
JS Objects
defined within a page across other pages. - You can't define variables and functions outside of the
export default { }
declaration. - If a function is async, it means that it returns a promise, so it can't be called on the fields incompatible with the return type, such as the Text property of the Text widget.