Hello everyone!
I'm trying to programmatically open widget that is in "Widget Controller".
I have tried
WidgetManager.getInstance().openWidget(widgetId)
and
const action = appActions.openWidget(widgetId);
getAppStore().dispatch(action);
Both attempts opened the widget, but only when the widget had been previously opened.
Is there a way how to open widget that was not yet opened?
I'm using ArcGIS Experience Builder Developer Edition version 1.5.
Thanks, Matěj.
Solved! Go to Solution.
This is something that was not possible in versions before 1.6.
https://developers.arcgis.com/experience-builder/sample-code/widgets/control-the-widget-state/
I'd also like to know of a solution. I have a widget I would like to have open when the app loads.
This is something that was not possible in versions before 1.6.
https://developers.arcgis.com/experience-builder/sample-code/widgets/control-the-widget-state/
Awesome! Thanks for the notice. Did 1.6 come out this week?
Robert do you know how we would tell the widget to open at a certain position within a map view?
Not have not looked into that yet
Hi @MatejKutik
Were you able to use the sample code to open the widget as the application loaded? You mentioned your sample worked but only if the Widget had been opened at least once.
In 1.13 you can load widget and then open it:
const widgetManager = WidgetManager.getInstance();
widgetManager.loadWidgetClass(widgetId);
widgetManager.openWidget(widgetId);
This code is very close to what I ended up with. There are two problems I ran into.
The first is, there is a difference between the widgetId of the source of the action (event), and the widgetId of the target of the action. I was having some serious troubles getting it to open. One thing I ran into with EXB 1.14 was that Because a widget could be on the screen or in the experience multiple times, it can be tricky to find the right one. Now, when you add them they all get a generic ID in the config.json something like widget_34 (where 34 is conceivably the count of items + 1 that have been added to the experience)
As you may guess, widget_34 is not a very descriptive ID. So I sought out a way to find a widget by its name in the client (not necessarily its uniqueId, but one that's more descriptive and came up with code like this:
/**
* Opens the results widget using the dynamically assigned widget ID.
*/
openResultsWidget = (): void => {
// Lookup the dynamically assigned widget ID in the Experience Builder configuration
// Where widgets is the folder under your-extensions, and your-widget-name is the folder name for the client widget
const widgetUri = 'widgets/your-widget-name/'
const widgets = getAppStore().getState().appConfig.widgets // Get the current app widgets config
// Find the widget with the specified URI
const widgetEntry = Object.entries(widgets).find(
([, widgetConfig]: [string, any]) => widgetConfig.uri === widgetUri
)
if (!widgetEntry) {
console.error(`No widget found with URI: ${widgetUri}`)
return
}
const [dynamicWidgetId] = widgetEntry // Extract the dynamically assigned widget ID (e.g., "widget_34")
console.log(`Found widget ID: ${dynamicWidgetId}`)
// Check if the widget class is already loaded
const isClassLoaded = getAppStore().getState().widgetsRuntimeInfo?.[dynamicWidgetId]?.isClassLoaded
if (!isClassLoaded) {
// Load the widget class dynamically
WidgetManager.getInstance()
.loadWidgetClass(dynamicWidgetId)
.then(() => {
getAppStore().dispatch(appActions.openWidget(dynamicWidgetId))
console.log(`Widget (${dynamicWidgetId}) opened after loading.`)
})
.catch((err) => {
console.error('Error loading widget class:', err)
})
} else {
// If already loaded, just open it
getAppStore().dispatch(appActions.openWidget(dynamicWidgetId))
console.log(`Widget (${dynamicWidgetId}) opened directly.`)
}
}
Now the other piece I was working on that I don't have a good working snippet for yet, I had designed the custom widget to look into appStore for certain things to be set, or changed and if present or changed, auto load data when opening. For some reason this action can launch before the appStore has the data, and its a timing issue I am already looking into.
@TimWestern The easiest way to find a widget_id is to look in the config.json. Here's how to find a sidebar: Open the config.json file in /server/public/apps/your-application-number/resources/config. Use ctl+F to find the word "Sidebar" and use that widget_id.
@JeffreyThompson2 Yes, you can find the current assigned id in the current configuration of an app, and in it will work, I don't doubt that. However, sometimes when you make a custom widget or action, and you may want to reuse this action or widget in other apps, it becomes important to know how to get that widgetId dynamically, because, that widgetID is assigned at the moment the widget is dragged into the dashboard.
Let's say its assigned widget_32 in one experience that you create. In the next one it could be widget_45. So while technically correct you could 'hard code' the id somewhere and use that and it would work, it would only work in that experience, meaning you have to repeatedly go in and check that assigned ID.
The other reason this could be important, is that if you have settings that get updated during development of a custom widget, or new config options, those config options often necessitate removal of the widget from the experience + saving in order to clear the previous config information, before dragging the updated widget in and then configuring those settings. As I have seen how quickly those IDs can change, in a more complex widget, relying on the assigned ID hard coded somewhere that way, means having to remember checking it every time you have to do that procedure.
Now for a lot of custom widgets, there may be no need to worry about that, just wanted to be clear, cause understanding the reason why I suggested a dynamic way to get it, is that it solves the problem in a specific set of circumstances when developing. I hope that clarifies.
(also in regard to what was going on with my action opening the widget and data not being there, I fixed that by having it poll and wait for the appStoreUpdate to be found, only then could I guarantee it was there in time when the Widget Opens. Because the way react handles state asynchronously you have no way to guarantee that data stored in that way, is available when the next command runs, without providing code to check or wait for it before executing other code which may need it. I was able to resolve that issue, in this manner.