Use data source in widget

Data sources define how your widget accesses data. There are a variety of things you may want to do with data sources, each of which are detailed below.

  • Select a data source and save the used fields in the widget settings
  • Read and display the data in the widget runtime
  • Filter or query data based on user input
  • Handle selection on data
  • Sync the data source with the ArcGIS Maps SDK for JavaScript feature object
  • Your widget may need to generate an output data source
  • Your widget may need to publish a message

Note: when you see the appConfig variable used in this guide, it refers to the app config JSON object. For ArcGIS Online or Enterprise edition, the app config JSON is saved as item data; For developer edition, it is saved under server/public/apps/[appId]/config.json (published version) or server/public/apps/[appId]/resources/config.json (draft version).

Select a data source in the widget settings

To select a data source in the widget settings, you should use the DataSourceSelector component. Experience Builder supports multiple types of data sources.

To use the DataSourceSelector component, you must set the data source types that the widget supports through the types prop. AllDataSourceTypes from jimu-core is the supported data source types. After you select a data source, you can get the selected data sources through the onChange callback. In the onChange callback, you need to call props.onSettingChange() to save the selected data sources into the appConfig (appConfig.widgets[widgetId].useDataSources). If the user adds a new data source, the newly-added data source is saved in appConfig.dataSources. See this sample widget.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
<DataSourceSelector
   types={Immutable([AllDataSourceTypes.FeatureLayer])}
   useDataSources={props.useDataSources}
   useDataSourcesEnabled={props.useDataSourcesEnabled}
   onToggleUseDataEnabled={onToggleUseDataEnabled}
   onChange={onDataSourceChange}
   widgetId={props.id}
/>

After selecting a data source, you may want to allow the user to select fields from the data. To do this, you can use the FieldSelector component. Like the data source, you should save the selected fields in appConfig.widgets[widgetId].useDataSources as well. The data source will automatically load these fields. For performance reasons, the fields that are not used won't be loaded.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
{
   props.useDataSources && props.useDataSources.length > 0 &&
   <FieldSelector
      useDataSources={props.useDataSources}
      onChange={onFieldChange}
      selectedFields={props.useDataSources[0].fields || Immutable([])}
   />
}

Read and display the data in the widget runtime

After a data source is selected in the widget settings, the widget runtime can get the selected data sources through props.useDataSources. To read the data, use the DataSource instance. To get the DataSource instance, use DataSourceManager or DataSourceComponent. To use DataSourceComponent, pass the useDataSource prop. To get the DataSource instance, use the onDataSourceCreated callback.

To read data, use the render function to display the data. See this sample widget. As an alternative, you can use the onDataSourceInfoChange callback and update your widget UI in this callback function according to the current data in the data source. If your widget needs to listen to the data source filter change, you can use onQueryRequired callback to update the data. Please note this callback will be valid only when the query prop is not used. If you want to update your widget only when the selected records changes, you can use onSelectionChange callback.

If your widget must load data, pass in the query and widgetId props. The widgetId is required because the framework uses this prop to manage the query parameters that multiple widgets apply to the same data source. If you want to load data but do not want to change the data in the data source, pass in the localId prop. This will create a local data source for use. The recommended localId pattern is: widgetId + ???, for example widget_1_my_local.

When you render the DataSourceComponent, the data source instance will be created but the data is not loaded initially because it's expecting you to pass in a query prop which would cause another network request. To get the loaded data, you can use dataSource.getRecords(). The data is paged and the default paging size is 100. The user can change the paging size in the builder data settings panel. The loaded data is cached on the client, and the cache will be cleared if the query criteria is changed.

When displaying the data, the displayed page size does not need to be the same as the query page size. For example, if the query paging size is 100, you can use dataSource.getRecordsByPage(1, 10) to get the first 10 records to display.

To get the total number of records, pass in the queryCount prop, then get the count through dataSource.count.

Whenever the data source info is changed, the data render function and the onDataSourceInfoChange callback will be invoked. The info includes the following:

  • instanceStatus: whether the data source instance is created successfully
  • status: whether the data is loading or loaded
  • countStatus: whether the count of data is loading or loaded
  • selectedIds: the selected data IDs
  • selectOptions: the query (filter) to select data, the data source will query it and put the results in the selectedIds
  • widgetQueries: the query (filter) widgets applied to the data source
  • version: the version number is used to manage the data change in the client side. Therefore, the data source consumers are aware when data is changed.
  • gdbVersion: for feature services that support branch versioning. When the Branch Version Management widget switches the branch version, the version is saved here.

In many cases, the widget will need to compare the current data source info with the previous info to determine what needs to be updated.

If your widget needs to listen to the data source info change, but does not need to use the data records in the data source, you can omit the query param and use the onQueryRequired callback to do the query.

To get the fields in the data source, you can use dataSource.getSchema().fields.

Filter or query data based on user input

When a widget filters the data, the data in the data source instance is changed and all widgets will observe the change. When a widget queries data through a data source, the data in data source instance is not affected.

When multiple widgets apply a filter to the same data source, the attribute filters are tied together with and operator(s). For geometry filters, the filter from the last widget (ordered by widget add order in the app config) to apply a geometry filter is used. For temporal filters, the intersection of time extents is used.

  • To filter a data source, there are two basic ways, depending on how the data source is configured:

    • If the widget loads data, the recommended way is to use the DataSourceComponent like:
    Use dark colors for code blocksCopy
    1
    2
    3
    <DataSourceComponent useDataSource={} widgetId={} query={}>
    { Your render method }
    </DataSourceComponent>

    You can find an example of using the DataSourceComponent in the Message subscriber sample and the Server-side output data source sample.

    Note: you can use dataSource.load() to load and filter the data as well.

    • If the widget does not load data, you can use the updateQueryParams() function of a Data Source. See the Filter feature layer sample for an example.
  • To query data through a data source, you can use dataSource.query(). When querying data, the filters applied to this data source are used as well.

Handle selection on data

The designed selection behavior in an Experience Builder app is that all widgets should update and observe the same selection. For example, when a user selects a record within a List widget, the Text widget should see the selection if it uses the selection view. Every data source has a selection data view that manages the selection. Besides the selection data view, the selected record ID is saved in the Redux app store so widgets that use the data source can get notified when the selection is changed.

To select data records in a data source, you can use dataSource.selectRecordById(), dataSource.selectRecordsByIds() or dataSource.selectRecords(). When using dataSource.selectRecordById() or dataSource.selectRecordsByIds(), if the records have been loaded in the data source, you don't need to pass in the second parameter. If not, you need to pass in the second parameter to make sure other widgets that use the selections can read the records. dataSource.selectRecords() can select data records by a query parameter. The data source will set the query result to the data source info so that other widgets can be updated accordingly.

To read the selection, you can use dataSource.getSelectedRecords().

Use WebMap/WebScene

WebMap and WebScene from the ArcGIS Maps SDK for JavaScript are wrapped as data sources in the jimu-arcgis package. To access a WebMap, use WebMapDataSource; to access a WebScene, use WebSceneDataSource. Review the MapView sample to learn how to use these data sources. In addition to the WebMap and WebScene objects, all the layers in these objects are wrapped as data sources as well, which allows you to call getChildDataSources() to get all the layer data sources. The supported layers and services are defined by SupportedLayerServiceTypes and SupportedServerTypes. If you have a ArcGIS Maps SDK for JavaScript layer and want to find the related layer data source, please call mapDs.getDataSourceByLayer() or mapDs.createDataSourceByLayer(). If you want to get child data sources of a specific data source type, please call mapDs.getDataSourcesByType(). Please note that if you create a map data source (WebMapDataSource or WebSceneDataSource) by the DataSourceComponent or the DataSourceManager, after the app is launched, the child data sources of it won't be created automatically. To create all child data sources, please call await mapDs.childDataSourcesReady().

Use FeatureLayer

In some workflows, you will be required to create a lightweight Experience that works with feature layers directly. In this scenario, you will use the FeatureLayerDataSource classes. In general, a widget using a standalone feature layer will get a FeatureLayerDataSource object without the layer property, however using a feature layer from a webmap or webscene will return a FeatureLayerDataSource object with the layer property. The layer object is from the ArcGIS Maps SDK for JavaScript. To get the popup info, use featureLayerDs.getPopupInfo(), to get the ID field, use featureLayerDs.getIdField(), to get the geometry type, use featureLayerDs.getGeometryType().

Use dark colors for code blocksCopy
1
2
3
4

 const getLayerObject = (ds: FeatureLayerDataSource) => {
    return ds.layer; // this can be null
 }

Sharing data between widgets

Widgets will often share the same data. A good illustration of this is using a Map and List widget in an experience. When a feature is selected in the List widget, the corresponding feature is selected on the map. The easiest way to accomplish this is to use the same data source for both widgets. For example, when an item is selected in the List widget, the widget will call datasource.selectRecordById() to update the data source info in the app store. This allows the Map widget to render the currently selected item accordingly. In addition, the currently selected item ID will be placed in the URL, which enables you to share the current app state with others.

Sync the data source with the ArcGIS Maps SDK for JavaScript feature object

In an Experience Builder widget you can use the ArcGIS Maps SDK for JavaScript to get features. Then you may want to allow other widgets to use those features. For example, you may need to highlight these features on map, or show these features in List widget. You have 3 options to do this, listed below.

  1. Select these feature records in a data source
    • If there is a data source instance you can use, you can just call dataSource.selectRecordsByIds() to select these records. If what you get is a Graphic instance, you need to create a FeatureRecord instance first (featureLayerDataSource.buildRecord(graphic)).
  2. Generate an output data source from your widget
  3. Publish a message
    • If your widget generates some features, you can publish the DataRecordSetChangeMessage message so other widgets can subscribe.

Work with MapView/SceneView or LayerView

In many cases, when your widget works with a data source, it may need to work with MapView/SceneView as well. After the widget gets a JimuMapView instance through JimuMapViewComponent, you can get the corresponding data source of the MapView/SceneView by jimuMapView.dataSourceId, and you can get the related layers through jimuMapView.jimuLayerViews. Through the JimuLayerView instance, you can get the corresponding data source of the layer view by jimuMapView.dataSource as well. Generally, to get a feature from a layer to sync with a data source, you have these options:

  • Use the ID field of the feature to find the related data records in the data source
  • Create a data record through the feature by creating a FeatureDataRecord instance
  • From a FeatureDataRecord instance, you can get the feature by using featureDataRecord.getFeature()

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.