Experience Builder can be extended through building custom widgets, creating custom themes, custom data sources, and message/actions. All of this is possible
by using Experience Builder's extensibility framework called
jimu, which is built on the following components:
- React + Redux framework
- Bootstrap 4 for user interface
TypeScript is required to develop widgets, themes, etc. in Experience Builder. It provides many advantages such as:
- Instant type error detection
- Better IDE Experience
- Self-documenting code
- More readable and easier to understand
jimu-corepackage loads and parses the app config, then loads the layout, theme, and widgets based on the app config. In order to support this, this package defines several classes like WidgetManager, ConfigManager, ThemeManager, etc. In addition, the jimu-core defines BaseWidget, some common types, and some extension points, which are subject to be extended by other packages and widgets.
jimu-layoutspackage contains common implementations for layout widgets.
jimu-uipackage contains all the UI components that experiences will utilize. Under the hood, it uses reactstrap. Additionally, more components that are not in reactstrap are added. To reduce the loading size, the components are split into some entries, such as index, setting-components, sql-expression-builder, etc. See API for details.
jimu-for-builderpackage supports developing the widget setting page.
Data source defines how your widget accesses data. For example, when data is from a remote server, the widget can use a data source class to query the data to the client side. When data is generated by a widget, it can be used by another widget by putting itself into a data source class.
At a high level, a data source has a schema, some records, and may have some child/parent data source. In addition, every data source has a type, id, and status
to help identify it. The
DataSource interface is defined in the
jimu-core package; it defines some of the following methods and properties:
id: the data source id.
type: property used in the widget to check which type of data source it is using.
fetchSchema: every data source must implement this method to return the schema. This is the schema defined in the remote data service. For example, when a user adds a data source in Experience Builder, the data source schema is not saved in the app config; instead, the
fetchSchemamethod is called to get the latest data source schema.
getSchema: method used by the widget to access the data source schema and fields in the data source.
getRecords: method used by the widget to access the data records in the data source.
getStatus: the widget will use this property to get the status of the data source. Some of these statuses include
A data source can have sub data sources. As a result, you may include more than one data source to make it easier to use. This kind of data source is called a
WebMapDataSource is a
DataSourceSet is also a type of data source once the
isDataSourceSet property is set to true.
Data source is managed by the
DataSourceManager to create and get data sources.
To simplify the use of a data source, a
DataSourceComponent component is defined. This accepts the
useDataSource property and returns the data source object and its status info through a callback.
It also accepts a function as its child, which can be used to get the data source object and info to render the data in the data source. The
DataSourceComponent component can also accept an optional
query property and will reload the data when the query is changed.
For data that requires query only, the
DataQueryComponent component can be used. It has the similar interface with
DataSourceComponent, but calls the
To support the most commonly used data formats such as a feature service, the API has a
QueriableDataSource interface and an abstract class
This interface has properties including
addRecord etc. The difference between the
query methods is such that
load updates the
records property and the status of the data source, while
query queries and returns the records only.
More specifically, we define two data sources in Experience Builder (ExB) to access a feature layer,
FeatureLayerDataSource. For instance, a standalone feature layer, will return a
FeatureQueryDataSource, and a webmap/webscene that contains a feature layer will return one or more
Both of these two data sources are
QueriableDataSource. The difference is that the
FeatureLayerDataSource has a
layer property and can be used to get the
In general, a data source is saved in two places: the data source object is saved and managed in
DataSourceManager, and the data source info is saved in a redux app store.
DataSourceComponent, the component will call
DataSourceManager to create the data source on demand, and return the data source object and
dataSourceInfo by using a callback prop.
dataSourceInfo, the data source's
selectedIds, etc. can be returned.
FeatureLayerDataSource, etc. are part of this package. These objects are accessed through a data source.
The data source provided by
RepeatedDataSourceProvider is called a repeated data source. All children widgets of the widget that provide a data source will receive the repeated data source.
This is similar to React's Context. A widget can access repeated data source by
this.props.repeatedDataSource. The repeated data source will get the data source's
Any widget can provide repeated data source by using
List widget in Experience Builder is a good example that provides a repeated data source.
To use a repeated data source, add the
supportRepeat property in a widget's manifest file.
JimuMapView to ensure a consistent extensibility model when creating widgets, message/actions, etc.
To create a
JimuMapView object, a widget can use the
JimuMapViewManager.createJimuMapView() method. A
JimuMapView object has these main properties:
view: the map/scene view object.
datasourceId: the data source (webmap/webscene) that creates the view
mapWidgetId: the widget that creates the object
jimuLayerViews: the layer view object wrapper
The widget that needs to use the map/scene view can use the
JimuMapViewSelector component in the setting page to select a map/scene view. This will also use
JimuMapViewComponent to access the
The page in an experience is not a physical HTML web page; it is a conceptual page, however, it behaves like an actual HTML web page. It is implemented by using a
div HTML tag.
An experience must have at least one page, and it can have multiple pages. There are two types of page layouts in Experience Builder: a full screen app page and a linear scrolling page; the former one looks like an application and the latter one looks like a web page.
The page contents (widget/section) are organized by layout rules, such as a fixed layout and flow layout. To make the page responsive on different screen sizes, Experience Builder supports the configuration of layouts by size modes. These are defined by
The configuration of each layout is an adaptation of the experience, which offers greater flexibility with more customization of the application and better usage
of the screen size. A good illustration of this design is the City Explorer template. When the application is viewed on a large and medium screen,
the map is visible and is included in the configuration; however, when it is viewed on a mobile device, the map widget is removed from the first page and is only displayed on the second page after a user selects a feature in the list widget.
Theme defines an experience's looking and feel. Experience includes out-of-the box(OOB) themes, and custom themes can be created. Learn more about theme development.
A widget is a configurable functional unit. It is the building block used to create pages for an experience. Experience Builder provides OOB widgets, such as map, button, list, etc., and custom widgets can be created. A widget usually provides a setting UI to allow users to configure its functionality in the builder environment. If a widget has configurable options but does not include a setting UI, a JSON editor can be used to configure it.
Learn more about widget implementation.
Message/action is a way to support communication between widget to widget, widget to framework, and framework to widget. Widget/framework can publish a message, and they can listen to a message as well.
A message is identified by
MessageType, which is defined by the jimu framework. There are some message types defined in jimu, such as
DataRecordsSelectionChange. There will be more message types in future releases.
Learn more about how to create a message/action.