I have a widget which lets the admin configure a few layers in the settings. My widget will use the layers from the settings to serve up arcgis-feature-form components and query for potential associated data under certain conditions.
The problem is my widget holds references to these layers as UseDataSources but needs an ArcGISQueriableDataSource to query for potential associated data and will need __esri.FeatureLayer for setting up arcgis-feature-form components. Additionally, if I want to have a feature form I probably need to validate if the current user as write access for the Feature Layer, and I need an __esri.FeatuerLayer to check this.
This seems like a common pattern which could be solved with DataSourceComponent or 3. It gets a little ugly when I need all 3 layers loaded and checked for permission before revealing widget features or the appropriate error message. The best solution seemed like nesting every DataSourceComponent, using a callback to extract the __esri.FeatureLayer and ArcGISQueriableDataSource from each and passing it down the nested chain.
I was wondering how I could clean this up so I let Claude (Opus 4.6) try bossing me around. It mostly gave me nonsense code but it introduced an undocumented jimu component called "MultipleDataSourceComponent". To learn more I did a keyword search in google and the ExB docs site and only came up with one reference in a response in a community question.
MultipleDataSourceComponent works very similarly to DataSourceComponent except it will not complete until add data sources in the component are loaded. This is exactly what I need to simplify my widget which requires all of its data sources before becoming accessible.
- Please note MultipleDataSourceComponent is only appropriate when more than 1 data source are required to use one feature of a widget. If part of a widget only needs 1 data source then simply use multiple DataSourceComponents to allow partial access to the widget features which require the single data source. -
In conclusion I made a helper component I can pass multiple UseDataSources at once to get ArcGISQueriableDataSources and __esri.FeatureLayers back when my widget requires it:
import { React, ArcGISQueriableDataSource, Immutable, UseDataSource, DataSource,
MultipleDataSourceComponent } from 'jimu-core'
interface ComponentProps{
passRenderer: (
qdss: {[id: string]: ArcGISQueriableDataSource},
layers: {[id: string]: __esri.FeatureLayer}) => React.ReactNode,
useDataSources: Immutable.ImmutableArray<UseDataSource>
widgetId: string
failRenderer?: () => React.ReactNode | null,
}
export function MultipleQueriableDataSourceComponent(
{passRenderer, useDataSources, widgetId, failRenderer}: ComponentProps) {
// place in MultipleDataSourceComponent to get ArcGIS layers from dss
const renderContent = (
passRenderer: (qdss: {[id: string]: ArcGISQueriableDataSource},
layers: {[id: string]: __esri.FeatureLayer}) =>
React.ReactNode,
failRenderer?: () => React.ReactNode | null) =>
(dss: {[id: string]: DataSource}) => {
// ref
const qdss: {[id: string]: ArcGISQueriableDataSource} = {}
const layers: {[id: string]: __esri.FeatureLayer} = {}
// loop data source and ids to validate
for(const [id, ds] of Object.entries(dss)){
// queryable to find capabilities and get layer
const qds = ds as ArcGISQueriableDataSource
const layer = qds?.layer as __esri.FeatureLayer
// validate
if (!layer) return failRenderer()
qdss[id] = qds
layers[id] = layer
}
return passRenderer(qdss, layers)
}
return <MultipleDataSourceComponent
useDataSources={useDataSources}
widgetId={widgetId}
>
{renderContent(passRenderer, failRenderer)}
</MultipleDataSourceComponent>
}
In my instance I have an advanced form for filling out information around a watershed where I need to query sources for road and river miles as well as check the user has write access to the layer for the form I'm going to present. Because the component returns each data source's apis in a reference to the UseDataSource ID I need to apply the ID to the response to reference the apis I need.
<MultipleQueriableDataSourceComponent
useDataSources={useDataSources}
widgetId={props.id}
passRenderer={(qdss, layers) => {
// no longer loading
setNewRecordFormIsLoading(false)
// get layer references
const recordLayer = layers[conf?.record_layer?.source?.[0].dataSourceId]
const roadLayer = layers[conf?.road_layer?.source?.[0].dataSourceId]
const riverLayer = layers[conf?.river_layer?.source?.[0].dataSourceId]
// validate again
if(!recordLayer || !roadLayer || !riverLayer)
return <p>Error loading layers</p>
// check write access
if(!recordLayer.capabilities?.operations?.supportsAdd
&& recordLayer.capabilities?.operations?.supportsUpdate
&& recordLayer.capabilities?.editing)
return <p>Account doesn't have access to create new records</p>
return <NewRecordForm
recordLayer={recordLayer}
riverLayer={riverLayer}
roadLayer={roadLayer}
mapView={mapView}
recordLayerConfig={conf.record_layer}
riverLayerConfig={conf.river_layer}
roadLayerConfig={conf.road_layer}
widget_id={props.id}
/>
}}
failRenderer={() => {return <>
{!newRecordFormIsLoading &&
<p>Your account doesn't have access to make Records</p>
}
</>}}
/>