Triggering queries inside a mapped value

I am not sure if what I am trying to do is impossible or if I am just making a minor mistake.

I have a table component, and in a column I have a mapped value expression like this:

{{ currentSourceRow.description }}

And I have a Javascript query which takes additionalScope. It looks like this:

lcliz_orchestrator.trigger({additionalScope:{locale:'es_ES',text_en:'translate this please'}})

But changing my mapped value table to something like this:

{{ lcliz_orchestrator.trigger({additionalScope:{locale:'es_ES',text_en:currentSourceRow.description}}) }}

Does not work at all; I get this sort of error:

Or while typing I get this:

This suggests to me that additionalScope, which is sort of function-like thing, is not really a first class function. And I know that real first class functions which live inside "Preloaded JS" cannot call all kinds of Retool things such as JS queries. For example it would be impossible for me to transform lcliz_orchestrator.trigger() into a real function lcliz_orchestrator() because inside it does a lot of complicated work calling all kinds of Retoolisms:
image

So....I don't know if what I want to do is impossible, or if there is another way or trick.

I tried putting the necessary code inside a transformer on the query that provides the row data for this table. But that does not work either. Either

lcliz_orchestrator.trigger({additionalScope:{locale:'es_ES',text_en:item.description});

fails because lcliz_orchestrator is "not defined", or

{{lcliz_orchestrator.trigger({additionalScope:{locale:'es_ES',text_en:item.description})}};

fails when lcliz_orchestrator is resolved but item.description, a local variable inside the transformer, is not resolved.

Transformers seem to be the same kind of second class execution environment as mapped values inside a component.

You can use a transformer instead and call it like this in the mapped value

{{ transformer1.data[i] }}

You will need to modify the code to force the transformer to return an array.

something like:

let tableData = {{ table1.data }}
let newArray = []

for (let i = 0; i < tableData.length; i++ {
    // your code here
    tableData[i].description
    newArray.push()
}

return newArray

Yep. I tried that but I can't call my localize "function" inside the transformer. The transformer seems to have the same limitation as the mapped value. A box where javascript can be executed but with serious constraints.

I can probably unroll all the code inside my localize function and inline it and make it work. But that's just so ridiculous. I made a javascript function that takes additionScope precisely so I could reuse the code in several places.

I would expect mapped values not allowing to trigger querys, for two reasons.

It will overwrittes the output for each run (row) and will need to be triggered constantly.

A workaround will be to modify your query to run at the beginning and be able to populate your table or make another separate query/transformer specific for that table

Hi Roland,

To clarify, are you wanting to have the translation query run for each row so to then provide the translated version to the table?

If so, I would recommend taking the data that populates the table and running it through your translator query prior to populating the table.

You can do this by running through the tables mapped data and running the localization query for each index and returning

const promises = tableData.map(async item=>{
   const translatedDescription = await localizeQuery.trigger({additionalScope:{locale:"es_ES", text_en: item.description }}); 
   
   return {
       ...item, 
       translatedDescription
   }
}); 

return Promise.all(promises)

I hope I've understood your use-case and let me know if this works for you!

Best,
Evan

1 Like

Yeah that's what I was trying to do in the transformer. Both transformers and success handlers are ways of chaining together long pipelines of queries. I find that "pattern" to be OK but not great. I end up with a lot of stuff like this:

A -> B -> C -> D.data (is finally displayed in some UI).

Then somewhere else I have the same pattern:

J -> K -> L -> M.data

But suppose B and K are basically the same logic? I often find myself with this sort of duplicated logic because the inputs and outputs are different; even though the inner logic is the same.

I was trying break this pattern by using additionalScope but now I see there are all sorts of other restrictions that fight against that.

I completely understand and will be sure to note this down to our product crew as something to consider moving forward.

If you could send me a few screenshots of your setup so I could pass them along as well, that could help build the case.

Best,
Evan

Hi Roland!

I wanted to follow up here in case you missed Evan's most recent message - if you have any screenshots of your setup to share, I can definitely pass those along to our product team. Thanks in advance!

Screen shots are a lot of trouble. I'm not even sure WHAT screenshots would matter here.

In any case the bigger question is why is "additionalScope" available in some places and not others? I find there are many cases where I could reuse some component or logic if only I could pass in parameters and sometimes that facility is there and sometimes it is not and in that case duplication is the only solution. Why?

Hi Roland!

I was notified that you attended office hours today, but please let us know if you have any follow-up questions moving forward! To recap what was shared there:

  1. https://docs.retool.com/apps/scripting-events/guides/javascript#trigger-a-query https://community.retool.com/t/how-to-use-additional-scope-additionalscope-in-your-queries/13343
  2. you define the additionalScope variable from your JS query or wherever you're triggering the query from! then, in your target query, you'll need to use the variable you just defined