Interface: DataProvider

Oracle® JavaScript Extension Toolkit (JET)
9.2.0

F30740-01

Signature:

interface DataProvider<K, D>

QuickNav

DataProvider

Version:
  • 9.2.0
Since:
  • 4.2.0
Module:
  • ojdataprovider

Module usage

See JET Module Loading for an overview of module usage within JET.

Typescript Import Format
//To use this interface, import as below.
import {DataProvider} from "ojs/ojdataprovider";
Generic Parameters
ParameterDescription
KType of Key
DType of Data

JET In Typescript

A detailed description of working with JET elements and classes in your typescript project can be found at: JET Typescript Usage.

Description

The DataProvider interface defines the contract by which JET components retrieve data. By exposing this contract as an interface, we allow for a range of possible data retrieval strategies, while shielding components from dependencies on any one particular implementation choice. For example, some DataProvider implementations may get data from a local array. Others may retrieve data from a remote endpoint. In either case, the consuming component simply interacts with the DataProvider interface and is unaware of the of the specific data retrieval approach.

The DataProvider contract has the following characteristics:

  • Asynchronous: Even in cases where data is available synchronously (eg. the data is already in a local array), the DataProvider contract provides access to the data via asynchronous APIs. As such, consumers are able to interact with the data in a consistent manner, regardless of how the data is retrieved.
  • Stateless: The DataProvider’s data retrieval APIs are inherently stateless. Attempts to retrieve data are atomic and are not impacted by previous interactions with the DataProvider. This avoids potential brittleness when multiple consumers are interacting with the same DataProvider instance.
  • Key-based: In order to ensure reliable interactions with the data set, the DataProvider contract assumes that each data item can be accessed via a unique key. While the index can be used as a key if no viable key is available, stable keys should be used whenever possible.
  • Read only (with mutation notifications): The base DataProvider contract does not include mutation APIs. That is, the DataProvider contract defines APIs for reading data, not for writing data. However, DataProvider implementations may expose their own type-specific mutation APIs, and the DataProvider contract defines an event-based mechanism for notifying consumers of data changes.
  • Filterable: When requesting data from a DataProvider, consumers are able to specify filter criteria that area used to restrict the data set to those items that match the specified criteria.
  • Sortable: When requesting data from a DataProvider, consumers are able to specify sort criteria that impact the ordering of the provided data.

The DataProvider contract exposes three ways for consumers to retrieve data:

  • Iteration: the DataProvider#fetchFirst method returns an AsyncIterable that can be used to iterate over the entire data set. Consumers typically use this when rendering a data set.
  • By keys: the DataProvider#fetchByKeys method allows specific items to be retrieved by key. Consumers typically use this when interacting with a subset of data (eg. for retrieving the values of the selected rows in a table component).
  • By offset: the DataProvider#fetchByOffset method allows a specific block of data to be retrieved by specifying an offset and size. Consumers typically use this for paging purposes.
A related interface is TreeDataProvider, which extends DataProvider. TreeDataProviders represent hierarchical data, whereas (non-tree) DataProviders represent data sets that are single-level.

JET provides several out-of-the-box DataProvider implementations that support the most common use cases.

Implementations

Class Description
ArrayDataProvider Basic DataProvider implementation that takes the data from an Javascript array or ko.observableArray.
ArrayTreeDataProvider Basic TreeDataProvider implementation that takes the data from an Javascript array or ko.observableArray that contains "children" property for subtree data.
CollectionDataProvider DataProvider implementation that takes the data from a oj.Collection object. oj.Collection is an older class that represents data usually comes from external source such as a REST.
DeferredDataProvider DataProvider implementation that takes the data from a promise that resolves to another DataProvider object.
FlattenedTreeDataProviderView DataProvider implementation that wraps a TreeDataProvider object and "flattens" the hierarchical data into a single level.
IndexerModelTreeDataProvider TreeDataProvider implementation that takes the data from an Javascript array that contains "children" property for subtree data. This class also implements the oj.IndexerModel interface.
ListDataProviderView DataProvider implementation that wraps another DataProvider, adding data manipulation functionality such as filtering, sorting and field mapping.
PagingDataProviderView DataProvider implementation that wraps another DataProvider object. This class also implements the oj.PagingModel interface so that it can be used by components that support paging.
TreeDataProviderView TreeDataProvider implementation that wraps another TreeDataProvider object and exposes additional APIs. This class provides field mapping functionality for the wrapped TreeDataProvider.

Class Hierarchy

Events

Implementations can fire the following events by creating an instance of the event class and passing the event payload in the constructor.

DataProviderMutationEvent

This event is fired when items have been added or removed from the data.

Event payloads should implement the DataProviderMutationEventDetail interface.

Consumers can add an event listener for the "mutate" event type on the DataProvider object.

Example of implementation firing an DataProviderMutationEvent for removed items:
let removeDetail = {data: removedDataArray,
                    indexes: removedIndexArray,
                    keys: removedKeySet,
                    metadata: removedMetadataArray};
this.dispatchEvent(new DataProviderMutationEvent({remove: removeDetail}));
Example of consumer listening for the "mutate" event type:
let listener = function(event) {
  if (event.detail.remove) {
    let removeDetail = event.detail.remove;
    // Handle removed items
  }
};
dataProvider.addEventListener("mutate", listener);

DataProviderRefreshEvent

This event is fired when the data has been refreshed and components need to re-fetch the data.

This event contains no additional event payload.

Consumers can add an event listener for the "refresh" event type on the DataProvider object.

Example of consumer listening for the "refresh" event type:
let listener = function(event) {
};
dataProvider.addEventListener("refresh", listener);

Custom Implementations

Applications can also create their own implementations of the DataProvider interface and use them with JET components. For example, an application can create a DataProvider implementation that fetches data from a REST endpoint.

Implementation classes must implement all of the interface methods. It should also fire the DataProvider events when appropriate, so that JET components or other consumers can respond to data change accordingly.

A generic implementation of DataProvider#fetchByKeys and DataProvider#containsKeys is available from FetchByKeysMixin which can be used in custom implementations of DataProvider. It is for convenience and may not provide the most efficient implementation for your data provider. Classes that implement the DataProvider interface are encouraged to provide a more efficient implementation.

Methods

addEventListener(eventType: string, listener: EventListener): void

Add a callback function to listen for a specific event type.
Parameters:
Name Type Description
eventType string The event type to listen for.
listener EventListener The callback function that receives the event notification.

containsKeys(parameters : FetchByKeysParameters<K>) : Promise<ContainsKeysResults<K>>

Check if there are rows containing the specified keys. The resulting key map will only contain keys which were actually found.
Parameters:
Name Type Description
parameters FetchByKeysParameters contains by key parameters
Since:
  • 4.2.0
Returns:
Returns Promise which resolves to ContainsKeysResults.
Type
Promise.<ContainsKeysResults>
Example

Check if keys 1001 and 556 are contained

let containsKeys = [1001, 556];
let value = await dataprovider.containsKeys({keys: containsKeys});
let results = value['results'];
if (results.has(1001)) {
  console.log('Has key 1001');
} else if (results.has(556){
  console.log('Has key 556');
}

createOptimizedKeyMap(initialMap?: Map<K, D>): Map<K, D>

Return an empty Map which is optimized to store key value pairs

Optionally provided by certain DataProvider implementations for storing key/value pairs from the DataProvider in a performant fashion. Sometimes components will need to temporarily store a Map of keys provided by the DataProvider, for example, in the case of maintaining a Map of selected keys. Only the DataProvider is aware of the internal structure of keys such as whether they are primitives, Strings, or objects and how to do identity comparisons. Therefore, the DataProvider can optionally provide a Map implementation which can performantly store key/value pairs surfaced by the DataProvider.

Parameters:
Name Type Argument Description
initialMap Map.<any> <optional>
Optionally specify an initial map of key/values for the Map. If not specified, then return an empty Map.
Since:
  • 6.2.0
Returns:
Returns a Map optimized for handling keys from the DataProvider.
Type
Map.<any>
Example

create empty key Map

let keyMap = dataprovider.createOptimizedKeyMap();

createOptimizedKeySet(initialSet?: Set<K>): Set<K>

Return an empty Set which is optimized to store keys

Optionally provided by certain DataProvider implementations for storing keys from the DataProvider in a performant fashion. Sometimes components will need to temporarily store a Set of keys provided by the DataProvider, for example, in the case of maintaining a Set of selected keys. Only the DataProvider is aware of the internal structure of keys such as whether they are primitives, Strings, or objects and how to do identity comparisons. Therefore, the DataProvider can optionally provide a Set implementation which can performantly store keys surfaced by the DataProvider.

Parameters:
Name Type Argument Description
initialSet Set.<any> <optional>
Optionally specify an initial set of keys for the Set. If not specified, then return an empty Set.
Since:
  • 6.2.0
Returns:
Returns a Set optimized for handling keys from the DataProvider.
Type
Set.<any>
Example

create empty key Set

let keySet = dataprovider.createOptimizedKeySet();

dispatchEvent(evt: Event): boolean

Dispatch an event and invoke any registered listeners.
Parameters:
Name Type Description
event Event The event object to dispatch.
Returns:
Return false if a registered listener has cancelled the event. Return true otherwise.
Type
boolean

fetchByKeys(parameters : FetchByKeysParameters<K>) : Promise<FetchByKeysResults<K, D>>

Fetch rows by keys. The resulting key map will only contain keys which were actually found.
Parameters:
Name Type Description
parameters FetchByKeysParameters fetch by key parameters
Since:
  • 4.2.0
Returns:
Returns Promise which resolves to FetchByKeysResults.
Type
Promise.<FetchByKeysResults>
Example

Fetch for keys 1001 and 556

let fetchKeys = [1001, 556];
let value = await dataprovider.fetchByKeys({keys: fetchKeys});
// get the data for key 1001
console.log(value.results.get(1001).data);

fetchByOffset(parameters: FetchByOffsetParameters<D>): Promise<FetchByOffsetResults<K, D>>

Fetch rows by offset

A generic implementation of this method is available from FetchByOffsetMixin. It is for convenience and may not provide the most efficient implementation for your data provider. Classes that implement the DataProvider interface are encouraged to provide a more efficient implementation.

Parameters:
Name Type Description
parameters FetchByOffsetParameters fetch by offset parameters
Since:
  • 4.2.0
Returns:
Returns Promise which resolves to FetchByOffsetResults.
Type
Promise.<FetchByOffsetResults>
Example

Fetch by offset 5 rows starting at index 2

let result = await dataprovider.fetchByOffset({size: 5, offset: 2});
let results = result['results'];
let data = results.map(function(value) {
  return value['data'];
});
let keys = results.map(function(value) {
  return value['metadata']['key'];
});

fetchFirst(parameters?: FetchListParameters<D>): AsyncIterable<FetchListResult<K, D>>

Get an asyncIterator which can be used to fetch a block of data.
Parameters:
Name Type Argument Description
params FetchListParameters <optional>
fetch parameters
Since:
  • 4.2.0
See:
Returns:
AsyncIterable with FetchListResult
Type
AsyncIterable.<FetchListResult>
Example

Get an asyncIterator and then fetch first block of data by executing next() on the iterator. Subsequent blocks can be fetched by executing next() again.

let asyncIterator = dataprovider.fetchFirst(options)[Symbol.asyncIterator]();
let result = await asyncIterator.next();
let value = result.value;
let data = value.data;
let keys = value.metadata.map(function(val) {
  return val.key;
});
// true or false for done
let done = result.done;

getCapability(capabilityName: string): any

Determines whether this DataProvider defines a certain feature.
Parameters:
Name Type Description
capabilityName string capability name. Defined capability names are: "fetchByKeys", "fetchByOffset", "sort", "fetchCapability" and "filter".
Since:
  • 4.2.0
Returns:
capability information or null if undefined
Type
Object
Example

Check what kind of fetchByKeys is defined.

let capabilityInfo = dataprovider.getCapability('fetchByKeys');
if (capabilityInfo.implementation == 'iteration') {
  // the DataProvider supports iteration for fetchByKeys
  ...

getTotalSize : {Promise.<number>}

Return the total number of rows in this dataprovider
Returns:
Returns a Promise which resolves to the total number of rows. -1 is unknown row count.
Type
Promise.<number>
Example

Get the total rows

let value = await dataprovider.getTotalSize();
if (value == -1) {
  // we don't know the total row count
} else {
  // the total count
  console.log(value);

isEmpty(): 'yes' | 'no' | 'unknown'

Returns a string that indicates if this data provider is empty. Valid values are:
  • "yes": this data provider is empty.
  • "no": this data provider is not empty.
  • "unknown": it is not known if this data provider is empty until a fetch is made.
Since:
  • 4.2.0
Returns:
string that indicates if this data provider is empty
Type
"yes" | "no" | "unknown"
Example

Check if empty

let isEmpty = dataprovider.isEmpty();
console.log('DataProvider is empty: ' + isEmpty);

removeEventListener(eventType: string, listener: EventListener): void

Remove a listener previously registered with addEventListener.
Parameters:
Name Type Description
eventType string The event type that the listener was registered for.
listener EventListener The callback function that was registered.