I have a table (tableMembers) and a form with a save button.
When the button is clicked, it should take some data and insert it into the Retool database, then select the newly inserted row.
Here's the script for the button click:
insertPeople.trigger({onSuccess:(data)=> {
let id = data.id["0"];
getPeople.trigger({onSuccess:()=>{
tableMembers.selectRow({mode:'key',indexType:'data',key:id});
}});
}})
Unfortunately, this doesn't work. While my 'id' column is a number column, I've tried passing the id in as a number and a string. Neither works. The only thing that works is when I pass in a hard-coded number.
No, I saw that bug earlier. However, my table is fully visible.
I think it's a race condition with rendering. Let's say that I have 4 items in the table (with ID's 1-4), and then I insert another item to the table, which would have an auto-incremented seq generated ID of 5.
If I hardcoded the ID between 1 and 4 : tableMembers.selectRow({mode:'key',indexType:'data',key:4});
Then, the row would be selected just fine.
However, if I hardcoded the ID of 5 (knowing that the new item would have a generated ID of 5): tableMembers.selectRow({mode:'key',indexType:'data',key:5});
Then, the row would not be selected.
Again, I'm wondering if the table hasn't been fully rendered and part of the DOM and therefore the item isn't available for selection.
So, I tried something, and it does indeed appear to be a race condition. Here's my updated code, and it works as expected. I'm not sure if this is considered "Solved" or if this is best practice, but at least it works:
insertPeople.trigger({onSuccess:(data)=> {
let id = data.id["0"];
getPeople.trigger({onSuccess:()=>{
setTimeout(()=> {
tableMembers.selectRow({mode:'key',indexType:'data',key:id});
}, 1000);
}});
}})
It would be great if someone from Retool could verify that this is allowed. I know with many web app frameworks, using setTimeout can be problematic.
Good catch. Coincidentally I had a similar issue a couple of days ago, I have a js query preparing data for an API post call and I added a simple script to my button,
I kept getting errors on my createInvoice trigger until I manually added the event handler within the query's on success out of the box section, which makes it work seamleslly. It does seem like a bug/race condition when manually writing the onSuccess function.
@Paulo, have tagged this as bug although not sure it is one.
Instead of using setTimeout, it would be ideal if the table component (along with others) had an onrender event.
Currently, the setTimeout assumes that the table will be fully rendered in under a second. However, if this isn't the case, the code won't select a row successfully. I think we should be able to tie into the complete lifecycle of each component.
Something like this, perhaps:
insertPeople.trigger({onSuccess:(data)=> {
let id = data.id["0"];
tableMembers.onrender(()=>{
tableMembers.selectRow({mode:'key',indexType:'data',key:id});
});
getPeople.trigger();
}});
(NOTE: I recognize that this code as-is would create a memory leak as it could potentially add multiple instances of the lambda to the onrender event. However, it's just a concept.)
I am thinking this could be solved with the following steps
Set the default value of the selected row to be conditionally equal to insertPeople.data?.id[0], if it exists, otherwise some other default value. Similar to this:
@Tess, Unfortunately, that won't work in my situation. The SQL query returns results as sorted by "[lastname], [firstname]" (the table columns are not sortable, everything is sorted at the query). So, there's no way to know where the new item will be placed in the array.
Hi @jdavis I'm not sure I am following - would you mind sharing screenshots or more details? Is the table paginated?
Since the insert query is returning the newly created id, you can use that as the default primary key to be selected. You'd need to have the primary key value set on the table.