utils.openApp not triggering queries on query parameter change

Hi there,

I created a multi-app application where you can edit multiple database entries.
To navigate between the pages we use the sidebar navigation and an event handler setting the selected item in a temporary state, and with a 100ms delay triggering a JavaScript query navigateMenu with the following code:

const item = selectedNavigationItem.value
if (!item || item === '') {
  return navigateMenu.trigger();
// Delete the selection as it is now available in this query

// Skip items without app configured
if (!item.targetApp) {
utils.openApp(item.targetApp.key, {
  queryParams: item.targetApp.params

Side-Note: The reasoning behind this, I was not able to get the clicked menu item into the JavaScript method, but this is not really the reason for this topic, as it is working.\

Here an example for editing a database entry of type Link in an app called link_editor:
Screenshot 2023-03-13 at 14.13.34

When I click on the menu item FAQ's from any app other than link_editor, e.g. a home app, the utils.openApp call is functioning as expected, and it navigates to link_editor?id=faq.
After loading the page, all automatic methods to fetch data are triggered, and everything is fine.

But if I am alread in the app link_editor?id=faq and want to navigate to, e.g. Website, the utils.openApp should open link_editor?id=website and trigger all automatic queries, but instead it only changes the url query parameter id to id=website and does not reload or trigger any queries whatsoever.

Initially I thought I could manually trigger the queries (which is quite unpractical as I am using a module to have the same sidebar in all apps), but even then it is having issues:

  • I am proxying the url param id into a temporary state currentLinkId with the initial value of {{ ulrparams.id }}. I understand this value is only set at the initial loading of the page, so it is expected.
  • I replaced the temporary state with a transformer holding the function return {{ urlparams.linkId }}, expecting every call of the transformer function to return the current url query parameter. This also did not work, probably because the {{ urlparams.linkId }} is replaced at page load with the acutal value?
  • I tried to sync the url param with a temporary state, using the built-in "URL Parameters" functionality, but this also doesn't work.

Now I am wondering if I am either implementing the functionality wrong, or if there is a bug in utils.openApp not triggering a page load when only changing the url query parameter.

Any help is appreciated, thanks!

Hey @philprime!

Have you explored using hash parameters and linking them to your module outputs (see docs here). Doing so might let you re-run your queries without needing to fully refresh your page.

Attached is an example of what this may look like that you can import and play around with!


Also, in response to your side-note, have you tried using await when you set the tempstate value in your query?

Hi @Kabirdas

thanks for your response and sorry for my late answer.

I went ahead and tried the same approach but with hash parameters and linked them to the temporary state:

Then I adapted the utils.openApp call to include the hash parameters and also await:

await utils.openApp(item.targetApp.key, {
  queryParams: item.targetApp.params,
  hashParams: item.targetApp.hashParams

When clicking on the navigation menu item in the module, the console log shows the following:

Therefore I am quite certain that utils.openUrl should be executed.

Unfortunately nothing happens and I am still stuck on the same page. The only difference in this approach is that now the URL isn't even updating anymore, or if it is, it is immediately changed back to the original value in the temporary state.

As your example is not working I created a minimal reproducable example app.
While testing I figured out that this is not even related to the modules, but simply that openUrl is changing the URL but not triggering the temporary state to be updated.

I am not allowed to upload a file here, so I can only offer you the example like this:

  "uuid": "6d7e324c-fa1f-11ed-b75e-bf799319c752",
  "page": {
    "id": 154777148,
    "data": {
      "appState": "[\"~#iR\",[\"^ \",\"n\",\"appTemplate\",\"v\",[\"^ \",\"isFetching\",false,\"plugins\",[\"~#iOM\",[\"text1\",[\"^0\",[\"^ \",\"n\",\"pluginTemplate\",\"v\",[\"^ \",\"id\",\"text1\",\"type\",\"widget\",\"subtype\",\"TextWidget2\",\"namespace\",null,\"resourceName\",null,\"resourceDisplayName\",null,\"template\",[\"^3\",[\"heightType\",\"auto\",\"horizontalAlign\",\"left\",\"hidden\",false,\"imageWidth\",\"fit\",\"showInEditor\",false,\"verticalAlign\",\"center\",\"tooltipText\",\"\",\"value\",\"**Value of ID:** {{ currentId.value || \\\"No ID\\\" }}\",\"disableMarkdown\",false,\"overflowType\",\"scroll\",\"maintainSpaceWhenHidden\",false]],\"style\",[\"^3\",[]],\"position2\",[\"^0\",[\"^ \",\"n\",\"position2\",\"v\",[\"^ \",\"container\",\"\",\"rowGroup\",\"body\",\"subcontainer\",\"\",\"row\",0,\"col\",0,\"height\",0.6,\"width\",3,\"tabNum\",0]]],\"mobilePosition2\",null,\"mobileAppPosition\",null,\"tabIndex\",null,\"^<\",\"\",\"createdAt\",\"~m1684924874172\",\"updatedAt\",\"~m1684925066678\",\"folder\",\"\",\"screen\",null]]],\"$main\",[\"^0\",[\"^ \",\"n\",\"pluginTemplate\",\"v\",[\"^ \",\"id\",\"$main\",\"^4\",\"frame\",\"^5\",\"Frame\",\"^6\",null,\"^7\",null,\"^8\",null,\"^9\",[\"^3\",[\"type\",\"main\",\"sticky\",false,\"isHiddenOnDesktop\",false,\"isHiddenOnMobile\",false]],\"^:\",[\"^3\",[]],\"^;\",null,\"^B\",null,\"^C\",null,\"^D\",null,\"^<\",\"\",\"^E\",\"~m1684924874202\",\"^F\",\"~m1684924874202\",\"^G\",\"\",\"^H\",null]]],\"currentId\",[\"^0\",[\"^ \",\"n\",\"pluginTemplate\",\"v\",[\"^ \",\"id\",\"currentId\",\"^4\",\"state\",\"^5\",\"State\",\"^6\",null,\"^7\",null,\"^8\",null,\"^9\",[\"^3\",[\"_persistedValueGetter\",null,\"_persistedValueSetter\",null,\"persistValue\",false,\"persistedValueKey\",\"\",\"value\",null]],\"^:\",null,\"^;\",null,\"^B\",null,\"^C\",null,\"^D\",null,\"^<\",\"\",\"^E\",\"~m1684924888381\",\"^F\",\"~m1684924888381\",\"^G\",\"\",\"^H\",null]]],\"$urlFragments\",[\"^0\",[\"^ \",\"n\",\"pluginTemplate\",\"v\",[\"^ \",\"id\",\"$urlFragments\",\"^4\",\"setting\",\"^5\",\"UrlFragments\",\"^6\",null,\"^7\",null,\"^8\",null,\"^9\",[\"^3\",[\"value\",[\"^3\",[\"id\",\"{{ currentId.value }}\"]]]],\"^:\",null,\"^;\",null,\"^B\",null,\"^C\",null,\"^D\",null,\"^<\",\"\",\"^E\",\"~m1684924912350\",\"^F\",\"~m1684924916397\",\"^G\",\"\",\"^H\",null]]],\"button1\",[\"^0\",[\"^ \",\"n\",\"pluginTemplate\",\"v\",[\"^ \",\"id\",\"button1\",\"^4\",\"widget\",\"^5\",\"ButtonWidget2\",\"^6\",null,\"^7\",null,\"^8\",null,\"^9\",[\"^3\",[\"horizontalAlign\",\"stretch\",\"clickable\",false,\"iconAfter\",\"\",\"submitTargetId\",null,\"hidden\",false,\"text\",\"Increment ID\",\"showInEditor\",false,\"tooltipText\",\"\",\"styleVariant\",\"solid\",\"submit\",false,\"iconBefore\",\"\",\"events\",[\"~#iL\",[[\"^3\",[\"event\",\"click\",\"type\",\"util\",\"method\",\"openApp\",\"pluginId\",\"\",\"targetId\",null,\"params\",[\"^3\",[\"uuid\",\"6d7e324c-fa1f-11ed-b75e-bf799319c752\",\"options\",[\"^3\",[\"hashParams\",[\"^I\",[[\"^3\",[\"key\",\"id\",\"value\",\"{{ +(currentId.value) + 1 }}\"]],[\"^3\",[\"key\",\"\",\"value\",\"\"]]]]]]]],\"waitType\",\"debounce\",\"waitMs\",\"0\"]]]],\"loading\",false,\"loaderPosition\",\"auto\",\"disabled\",false,\"maintainSpaceWhenHidden\",false]],\"^:\",[\"^3\",[]],\"^;\",[\"^0\",[\"^ \",\"n\",\"position2\",\"v\",[\"^ \",\"^<\",\"\",\"^=\",\"body\",\"^>\",\"\",\"row\",0.5999999999999999,\"col\",0,\"^?\",1,\"^@\",3,\"^A\",0]]],\"^B\",null,\"^C\",null,\"^D\",null,\"^<\",\"\",\"^E\",\"~m1684924943040\",\"^F\",\"~m1684924991142\",\"^G\",\"\",\"^H\",null]]]]],\"^E\",null,\"version\",\"2.121.0\",\"appThemeId\",null,\"appMaxWidth\",\"1560px\",\"preloadedAppJavaScript\",null,\"preloadedAppJSLinks\",[],\"testEntities\",[],\"tests\",[],\"appStyles\",\"\",\"responsiveLayoutDisabled\",false,\"loadingIndicatorsDisabled\",false,\"urlFragmentDefinitions\",[\"^I\",[[\"^3\",[\"name\",\"id\",\"value\",\"{{ currentId.value }}\"]]]],\"pageLoadValueOverrides\",[\"^I\",[[\"^3\",[\"name\",\"currentId.value\",\"value\",\"{{ urlparams.hash.id }}\"]]]],\"customDocumentTitle\",\"\",\"customDocumentTitleEnabled\",false,\"customShortcuts\",[],\"isGlobalWidget\",false,\"isMobileApp\",false,\"multiScreenMobileApp\",false,\"mobileAppSettings\",[\"^ \",\"mobileOfflineModeEnabled\",false,\"mobileOfflineModeDelaySync\",false,\"mobileOfflineModeBannerMode\",\"default\",\"displaySetting\",[\"^ \",\"landscapeMode\",false,\"tabletMode\",false]],\"folders\",[\"^I\",[]],\"queryStatusVisibility\",false,\"markdownLinkBehavior\",\"auto\",\"inAppRetoolPillAppearance\",\"NO_OVERRIDE\",\"rootScreen\",null,\"instrumentationEnabled\",false,\"experimentalFeatures\",[\"^ \",\"sourceControlTemplateDehydration\",false],\"experimentalPerfFeatures\",[\"^ \",\"serverDepGraphEnabled\",false,\"useRuntimeV2\",true,\"runtimeV2OptOut\",false],\"experimentalStabilityFeatures\",[\"^ \",\"lockModelForUpdates\",false],\"experimentalDataTabEnabled\",true]]]"
    "changesRecord": [
      { "type": "WIDGET_REPOSITION2", "payload": { "moves": [{ "move": {}, "widgetIds": [] }], "largeScreen": true }, "hideChangelogEntry": false },
      { "type": "PLUGIN_DELETE", "payload": ["query1"], "hideChangelogEntry": false }
    "gitSha": null,
    "checksum": null,
    "createdAt": "2023-05-24T10:44:56.339Z",
    "updatedAt": "2023-05-24T10:44:56.339Z",
    "pageId": 2108491,
    "userId": 311830,
    "branchId": null
  "modules": {}

Looking forward to your feedback if this is misconfigured by me or a bug on your side.

Oh interesting - it does look like hash parameters aren't updating the values of temp states properly. Thanks for surfacing that, it does repro on my end and I've gone ahead and filed a bug. Instead of making references to the temp state would it work to directly reference {{ urlparams.hash.id }} for now?

I had the same problem here, nothing with urlparams or triggering the query with additionalState worked, but in the end I changed the event handler to set a localStorage variable, and then changed the query to use localStorage.

localStorage.setValue('myValue', tableX.selectedRow?.value)


{{localStorage.values.myValue ?? ''}}

You can use set a URL Param (App Settings > URL Parameters) to the value from localstorage, so your URL will link to the correct query result.

You will probably also want a script resource that runs on page load to work out how to handle any url param and update/clear local storage, or you'll end up with the value to previously had in local storage.

1 Like

Thanks for adding your workaround here @DaveMeehan! It looks like it's expected that the parameter to state link only works on page load and there aren't plans to change that behavior at the moment as it could break existing apps. For now, it looks like using one of the suggestions here may be your best bet.

Actually I found that using hash parameters does work as expected, its just the query params that don't.

In the query, I can do /path/{{urlparams.hash.myValue}} and it works as expected. In the event that triggers it, I use 'openApp' and specify the myValue hash param and its value.

Using localStorage wasn't so easy as it will remember the last value, and you don't seem to be able to set a page load script that can override that with a url or hash parameter before the query is run.

In my case I was able to "fix" the issue by manually replacing navigation with url params to hash params, and replacing the state variables with transformers.

This seems to trigger my queries, but does require me to manually update all of my navigation calls in all of my 20+ apps. Quite unfortunate that Retool will not provide a solution for such a core issue.

Got it, thanks for sharing your workaround here, even though it's a cumbersome one! Multi-screen apps are a highly requested feature and could definitely use some care, the dev team is very aware of it and is looking at building out features later in the year to improve support for them.