This widget is designed to serve a similar function to the @RobertScheitlin__GISP Feature Panel widget and was heavily inspired by it. Like the Feature Panel widget, it is designed to replace the popup and work in an auto-opening and closing sidebar. It differs in four upgrades and one downgrade.
Upgrades
- It is written in function based React. (This won't matter to end users, but it should make further modifications easier on you.)
- The results are displayed in tabbed form by layer making finding what you are looking for easier.
- There is an off button. (Have you ever tried using the Feature Panel Widget with the Draw or Measure Widgets? It's impossible. That's why end users can turn this widget on and off at will.)
- It is designed to get data from a lightly modified Search Widget. (Also included in the zip file. Yes, this is a 2 for 1 custom widgets deal.)

The Downgrade
It does not auto-detect what sidebar it is placed into. Why? Have you looked at @RobertScheitlin__GISP's code? More than half of it is dedicated to pulling off this really neat trick and I am way too lazy to try and translate all of it into functional React. (Also, his code doesn't quite work. At least, if you are using a layout with multiple sidebars.)
You will need to find the id number of your sidebar widget for the auto-sidebar to work. How?
- The widgets in Experience Builder are sequentially numbered in the order they are added, so if you add your Sidebar Widget immediately before adding the Identify Widget, you can subtract one from the widget_id shown in the Identify Settings Panel.
- 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.
How Identify works
The Identify Widget is split into three levels. The top level is dedicated to creating/destroying event listeners and sending new clicks down to the second level. In the second level, the screen point is sent to popup.fetchFeatures() (that calls hitTest() in the background) and gets the popupTemplate from all the featureLayers. Each identified feature is given a Feature Widget from the Javascript API that is packaged into an array and sent down the the third level. The second level is also responsible for adding and removing highlights from the map. The third level uses dangerouslySetInnerHTML to make the tabbed results display. (Note: dangerouslySetInnerHTML is no more dangerous than the standard .innerHTML() method. The React Devs named their version this way to remind developers that anything sent to the .innerHTML() method will be run as code, so it must come from a trusted source.)
How Search Works
The included Search Widget is the standard ESRI Search Widget with three lines of code added to the result-list.tsx onSelectRecord() function. One of which is just an aesthetic UI improvement to close the search result panel after the user makes a choice.
//Extracts geometry from data source
const geometry = records[0].feature.geometry
//Closes search result panel
onShowResultButtonclick()
//Sends geometry to identify widget
MutableStoreManager.getInstance().updateStateValue(`widget_${props.config.identifyWidgetId}`, 'newSearch', geometry)
It extracts the geometry from the data source and sends it to the Identify Widget where it is converted to a screen point and triggers the rest of the identify functionality. This Search Widget was designed and tested using a Locator source. It should also work with a point type layer source. It will not function properly with non-geographic data or other types of geometries.
You will need to enter the Identify widget_id into the Search Settings Panel. It can be easily seen in the Identify Settings Panel. Because Identify relies on hitTest(), you must also use the Zoom To Action to ensure that the searched location is visible on the screen. You may need to use a custom zoom level as well. If you do not zoom into a features visibility range, the feature will not be displayed and won't be found by hitTest(). And hitTest() is, by design, not super precise and will find all features in a six pixel buffer around the actual screen point, so you may need to zoom closer than the default to prevent finding more features than desired.
The Room For Improvement
At this time, I have no intention of doing additional work on these widgets, but should anyone else want to take a take a crack at improving them here are the rough spots.
- Personally, I have never been very good at working with Promises, so I needed to use setTimeout() to await the processing of the results. (The default for this timeout is 2 seconds and can be adjusted in the Identify Settings Panel. Setting this timeout too low will result in missing data and setting it too high will cause the user to wait unnecessarily. You will need to adjust this number to account for the speed of your server and network.) If a better programmer can figure out how to make this widget properly await the resolution of all the promises, I would appreciate it.
- Identify works best if it is placed in a sidebar partly for the aesthetic of keeping the map clean and visible, but also because between the users click and the final processing of the results, the old results will be displayed with no visual indicator to the user that the widget is working. The sidebar hides this problem by closing when a click/search is first passed to Identify and re-opening when the new results are ready. (I attempted to use the Loading Widget from jimu-ui, but could not find a way to make Loading load and unload correctly in all circumstances.)
- An on-map loading indicator would also be nice.
- There may be some ways to make the widget more efficient. There are some calls to the clearHighlights() function that were needed at some point in development, but I'm not sure are needed in the final version. I'm also not sure the identifyDiv does anything in the final version other than displaying a line of text before the first search.