Need help with setting up bulk action panel in Retool Table

Goal: I am attempting to create a bulk action panel within Retool that allows users to modify numeric attributes (set value, change by value, change by percentage) and a boolean attribute (PAUSED/ACTIVE) for selected rows in a web table. This panel should facilitate quick updates without altering the database directly, with an option to review changes before final submission via API post request.

Steps: Currently, I've explored Retool's documentation on table operations and forms but haven't found a clear path to implement a bulk action panel as described. I've tried configuring individual form elements and linking them to table selection events but am unsure how to consolidate these into a cohesive bulk action form.

Details: I'm using Retool's cloud-hosted version. The components involved include a web table displaying data fetched from an API, alongside form elements (number inputs and a toggle switch) for attribute modification. I've attempted to use Retool's JS Query Editor for data manipulation but haven't achieved the desired bulk update functionality.

Something like that:

Any idea how can I do so?

Hello @R_S_I_F .

If I am interperting your issue correctly, this is how I would implement this in an app:

  • Create a new JS query that will do the following:
    • Cycle through all selected rows with table1.selectedSourceRows.map()
    • Set the values yourTable.selectedSourceRows.map(x => ({...x, bid_amount: selectBidAmount.value, budget: selectBudget.value})) (repeat for the rest of the fields)
  • grab the result from the query and display in a new table inside a modal or another place to display it.
  • ask for confirmation before sending the data via API request.

Dont forget to change all the component names to the ones in your app!

I hope this helps, let me know if you have any difficulties or follow-up questions.

2 Likes

Hey thanks!
for some reason, i don't see the update on the table

if (changebid.value != 0) {
  if (bidtype.value === '%') {
    console.log("aaa");
     maintable.selectedSourceRows.forEach((row) => {
      row.bid_amount *= (1 + changebid.value / 100);
    });```

Any idea why?

Do you see the console log?

You can't change the table directly like that, you would have to create a new table with the info or create a interchangeable variable that acts as a datasource for the table and changes when you run the code and reverts back to the original data from db after you sent the new data via API

i see the console log.

so I need to create a temporary table?
how can I do so?

create a new variable on the code part of the app:

image

then when you transform the table data you can do state1.setValue(data)

and create a modalFrame within the app with a new table with state1.value as its data source

so I need to enable transform results on the query? and then on the data source of the table, I should set it to the qeuryname.data instead directly to the query?

and then set the JS to:

if (changebid.value != 0) {
  if (bidtype.value === '%') {
    console.log("aaa");
     today.data.selectedSourceRows.forEach((row) => {
      row.bid_amount.setValue(row.bid_amount*(1 + changebid.value / 100));
    });

I would create a new state(variable) for that data.
Then set its value on the button of the filters.
Then create a new table on a modalFrame with that variable as it's data source

But I want that when the update button is clicked it will make the changes on the main table.
And then if the user clicks on a second button it will show you 'are you sure' screen with all the changes made on the main table (bulk changes and non-bulk), and if the user clicks 'send' it will send the API request with all the changes that need to be done.

Then I think the best way is still to create a state, on the original query where you fetch the data for the table do an event handler that sets that variable to the table data. When you make changes you override that variable value to the one with the changes. After you submit API request or whenever you want to revert changes you just need to run the original query again.
That variable should also be the datasource for your table

Ok, so how can I set that every time a query runs, it sets the state var's value as the query's results?

on the query tab of retool, scroll down to the bottom, you have the event handlers section. You create a new one, set the action to "set variable", select your var and set the value as {{self.data}}

I tried to do so but setValue doesn't work.

Error:todaydata.value.bid_amount[row].setValue is not a function

maintable.selectedDataIndexes.forEach((row) => {
      todaydata.value.bid_amount[row].setValue(maintable.selectedRows[row].bid_amount*(1 + changebid.value / 100))
    });```

You need to set value for the whole variable, something like:

maintable.selectedDataIndexes.forEach((row) => {
todaydata.setValue(todaydata.value.map((item, index) => index == row ? ({...item.value, bid_amount: maintable.selectedRows[row].bid_amount*(1 + changebid.value / 100)} : item)});

Something like this? you might need to make some adjustments

I get errors when doing it...

Sorry @R_S_I_F, I can't help without knowing the errors and variable names, you might need to adjust the code to your specific needs.
If you are able to, you can send an app export and I can check it directly.

the script is:

  for (let i = 0; i < maintable.selectedDataIndexes.length; i++) {
      console.log(i+" source: "+todaydata.value.bid_amount[maintable.selectedDataIndexes[i]]);
      console.log(i+" target: "+maintable.selectedRows[i].bid_amount*(1 + changebid.value / 100));
      todaydata.value.bid_amount[maintable.selectedDataIndexes[i]].setValue(maintable.selectedRows[i].bid_amount*(1 + changebid.value / 100));
                                                                           
    } 

todaydata is the var that stores the table data (used as the data source of the table)
the idea here is to change the bid_amount value on the row selected by changebid.value %

But I get

Error:todaydata.value.bid_amount[maintable.selectedDataIndexes[i]].setValue is not a function

The error that you get is due to the function setValue being use to set a part of a variable's value. You need to set the whole variable, like so:

maintable.selectedDataIndexes.forEach( (item,index) =>
console.log(index+" source: "+todaydata.value.bid_amount[maintable.selectedDataIndexes[index]]);
      console.log(index+" target: "+maintable.selectedRows[index].bid_amount*(1 + changebid.value / 100));

todaydata.setValue(todaydata.value.map((x,y) => y == index ? ({...x, bid_amount: item.bid_amount*(1 + changebid.value / 100)}) : x)
)                                                                       

Maybe something like this? It's hard to know for sure just looking at your code like this

Please make sure all the parenthesis are closed and that the variable names are correct.

I get an error:
todaydata.value.map is not a function

what is todaydata.value? Can you send a screenshot?