Retool Mobile Fundamentals: Lesson 5

Learn how to extend your app's functionality with JavaScript queries and transformers.

As you've learned, anything between curly brackets {{ }} is processed as JavaScript in Retool. This is great for smaller pieces of code, but sometimes apps require more complex logic. This is where transformers and writing custom JavaScript code come in.

Transformers allow you to write reusable pieces of code. They're often used to manipulate data, and you can access the transformer throughout your app using {{ transformer.value }}. Transformers execute their code automatically, and aren't explicitly triggered with event handlers or components.

You can write more complex logic using JavaScript. This allows you to trigger queries and configure components, which gives you more control over how your app works. If you create an app with small pieces of code spread throughout it, you might want to write JavaScript queries to centralize that logic instead.

1. Add search functionality

On the Users screen, it'd be useful to search for users instead of scrolling through them. JavaScript transformers update automatically when the object they reference changes, which means you can write a transformer to read the value in a Text Input component and then filter users based on the text.

First, add a Text Input component to the top of the Users screen. By default, the component is added to the bottom of the screen but you can drag and drop components in the left panel to reorder them. Update the Label to Search users.

To add a JavaScript transformer, click the + button in the Query Editor and select JavaScript transformer. Name it searchUsers and paste the following code into the editor.

const users = {{ getUsers.data }};

const result = users.filter(user => user.name.toLowerCase().includes({{textInput4.value}}.toLowerCase()));

return result;

Writing a JavaScript transformer

This code iterates through each user returned in the getUsers query, and checks to see if the value in the textInput4 component is contained in the name. The values are converted to lowercase with toLowerCase() so that search terms don't have to match case exactly.

Make sure to save the transformer, then select the collectionView1 component on the Details screen. Update the Data source to searchUsers and the Body to {{ item.blocked ? 'Blocked' : 'Allowed' }}. You can test the search functionality by selecting the Text Input component and typing.

Using a JavaScript transformer to add search functionality

2. Add functionality to block and unblock all users

In a previous step, you added an action to the Users screen for blocking and allowing all users. It's now time to connect that action to some custom JavaScript to perform these bulk actions. The JavaScript queries you'll write identify which users to block or allow and then trigger the appropriate query.

Create a JavaScript query

Start by clicking the + button in the Query Editor and selecting JavaScript query. Name it blockAll and then paste this code into the editor. Make sure to save the query.

var users = getUsers.data;

const unblockedUsers = users
  .map((user, index) => [user, index])
  .filter(([user]) => !user.blocked);

let blockIndex = 0;

const blockNext = () => {
  // Notify and quit when finished
  if (blockIndex >= unblockedUsers.length) {
    utils.showNotification({
      title: "Success",
      description: "Successfully blocked " + unblockedUsers.length + " users.",
      notificationType: "success",
      duration: 3,
    });

    // Update table after queries finish

    getUsers.trigger();

    return;
  }

  // Get next unblocked user and unblock them
  const [unblockedUser, index] = unblockedUsers[blockIndex];
  blockAllUsers.trigger({
    additionalScope: { i: index },
    onSuccess: function () {
      blockIndex++;
      blockNext();
    },
  });
};

blockNext();

This code finds unblocked users, blocks them with the blockAllUsers API query (which you'll create in a moment), and then displays a notification with the number of users unblocked. There are a few more things to note:

  • The i variable is the index of the current user. This will be used in the blockAllUsers query to block the user.
  • additionalScope allows you to pass parameters to your REST query. In this example, it passes the i variable to the blockAllUsers.
  • utils.showNotification is a built-in JavaScript method provided by Retool. You can read more about it and other methods in our JavaScript API Reference.

Adding a JavaScript query

Create a query to block all users

Next, create another PATCH query and name it blockAllUsers. Set the endpoint slug to {{ getUsers.data[i].id }}, and set blocked to true in the JSON body. Make sure to save the query.

Adding a block query that's triggered by a JavaScript query

This query uses the i value passed from the JavaScript query to unblock the appropriate user ID.

By default, queries show notifications after they run. If you're running several queries in succession, you might want to turn these notifications off. To do this, make sure the blockAllUsers query is selected and open the Response tab. Scroll down to Query success and uncheck the Show notification on success box. Make sure to save the query.

Turning off success notifications

With all the code written, open the Users screen and select the Interface User Lock action. Click + New in the Actions section. This creates a default trigger action. Select this action then:

  1. Set the Text field to Block all.
  2. Set the Query to the blockAll JavaScript query.

Configuring an action to run a query

Add functionality to allow all users

You can add functionality to allow all users by duplicating what you just created and swapping the blocked logic to unblock users. Copy the code below to create the JavaScript query to unblock all users.

var users = getUsers.data;

const blockedUsers = users
  .map((user, index) => [user, index])
  .filter(([user]) => user.blocked);

let unblockIndex = 0;

const unblockNext = () => {
  // Notify and quit when finished
  if (unblockIndex >= blockedUsers.length) {
    utils.showNotification({
      title: "Success",
      description: "Successfully unblocked " + blockedUsers.length + " users.",
      notificationType: "success",
      duration: 3,
    });

    // Update table after queries finish

    getUsers.trigger();

    return;
  }

  // Get next blocked user and unblock them
  const [blockedUser, index] = blockedUsers[unblockIndex];
  unblockAllUsers.trigger({
    additionalScope: { i: index },
    onSuccess: function () {
      unblockIndex++;
      unblockNext();
    },
  });
};

unblockNext();

Wrap up

You've now finished your app and completed Retool Mobile Fundamentals.

You can test your knowledge below or see the wrap up page for additional resources.


What’s Next

You've now completed all the lessons for Retool Mobile Fundamentals. Continue reading to wrap up the course and recap your progress.