Overview
DevExtreme includes a data layer, which is a set of complementary components that enable you to read and write data. The core data layer objects are DataSource and Stores.
DataSource simplifies working with data, whether it be an in-memory array, a local browser storage, remote service, or any custom storage. DataSource is a stateful object that maintains sorting, grouping, filtering, and data transformation properties and applies them each time data is loaded. It also includes events intended to handle changing data and the state.
The DataSource's underlying data access logic is isolated in a Store. Unlike the DataSource, a Store is a stateless object that implements a universal interface to read and modify data. Each Store contains the same set of methods. For more information about Stores, refer to the What Are Stores section of this article.
The following picture illustrates how the DataSource and Stores interact.
Asynchrony
All data transfer operations in the data layer are asynchronous for both remote services and local data access. This setup supports the universal Store interface for any data source. The data transfer operations return a native Promise. Use this Promise to specify callbacks for successful operation completion and operation failure.
- dataSource.load()
- .done(function(result) {
- //success callback
- })
- .fail(function(error) {
- //error callback
- });
Creating DataSource
To create a DataSource instance, call the DataSource constructor and pass the required configuration object. This section explains how to link the required data with the new DataSource instance. Use one of the following properties to create the DataSource instance.
From Array
Pass the required array to the DataSource constructor.
- const dataSource = new DevExpress.data.DataSource(array);
If you need to specify other DataSource configuration properties, use another form.
- const dataSource = new DevExpress.data.DataSource({
- // array of data
- store: array,
- // additional configuration properties
- sort: "name",
- pageSize: 10
- });
In this case, the array is passed to the store property of the configuration object.
From url
If you pass a string to the DataSource constructor, the DataSource will access the data returned by the AJAX request to the URL specified by this string.
- const dataSource = new DevExpress.data.DataSource("http://www.example.com/dataservices/jsondata");
You can also use a JSONP callback parameter.
- const dataSource = new DevExpress.data.DataSource("http://www.example.com/dataservices/jsonpdata?callback=?");
See Also
From Store
Pass a Store instance to the DataSource constructor.
- const store = new DevExpress.data.ArrayStore(data);
- const dataSource = new DevExpress.data.DataSource(store);
If you need to specify other DataSource properties, use the following code:
- const store = new DevExpress.data.ArrayStore(array);
- const dataSource = new DevExpress.data.DataSource({
- sort: "name",
- pageSize: 10,
- store: store
- });
In this example, the Store instance is passed to the store configuration property.
A Store can be specified implicitly. Two methods are described above (From Array and From Url). You can also use the following methods:
Add the properties implementing the required data access logic to the DataSource configuration object. In this case, the CustomStore will be automatically created within the DataSource. The properties should correspond to the CustomStore configuration properties.
JavaScript- const dataSource = new DevExpress.data.DataSource({
- //DataSource configuration
- sort: "name",
- pageSize: 10,
- //data access logic
- load: function(loadOptions) {
- return array;
- },
- byKey: function(key) {
- return array[key];
- },
- ...
- });
Pass a Store configuration object. This object should include the required Store configuration properties and the type property. type specifies which Store object will be created within the DataSource.
JavaScript- const dataSource = new DevExpress.data.DataSource({
- store: {
- type: "array",
- data: array
- }
- });
The following Store types are available.
- "array" - creates an ArrayStore
- "local" - creates a LocalStore
- "odata" - creates an ODataStore
What Are Stores
A store is a universal data access component within the DevExtreme data layer. It includes the following methods that are required for reading and editing data:
- load(options) - Loads data. This function accepts an object specifying sorting, grouping, filtering, and data transformation properties.
- insert(values) - Adds a new item with the passed values.
- remove(key) - Removes the data item the key specifies.
- update(key, values) - Sets the new values to the data item the key specifies.
- byKey(key, extraOptions) - Loads the data item the key specifies. The extraOptions argument requires additional implementation properties to get the required item.
- totalCount(options) - Enables you to get the total count of items that satisfy the specified conditions without loading them. The options object can contain filter and group fields that specify filtering and grouping properties.
DevExtreme includes the following Store implementations for the most common data access scenarios:
- ArrayStore - provides access to an in-memory array
- LocalStore - provides access to HTML5 web storage
- ODataStore - provides access to a remote OData service
You can implement custom data access logic as described in the Custom Sources topic if these stores are not suitable.
Reading Data
For simplicity, this section uses an in-memory array to explain data reading:
- const dataSource = new DevExpress.data.DataSource([
- { name: "item1" },
- { name: "item2" },
- { name: "item3" }
- ]);
This section applies to any data source.
To load data, call the dataSource.load() method.
- dataSource.load()
- .done(function(result) {
- // 'result' contains the array associated with the DataSource
- })
- .fail(function(error) {
- // handle error
- });
The DataSource allows you to specify initial data shaping properties (sort, filter, etc.).
- const dataSource = new DevExpress.data.DataSource({
- store: [
- { name: "Charlie", value: 10 },
- { name: "Alice", value: 20 },
- { name: "Bob", value: 30 }
- ],
- filter: [ "value", ">", 15 ],
- sort: { field: "name", desc: true }
- });
In the ArrayStore, LocalStore, and ODataStore data shaping operations are applied in the following order:
The CustomStore passes data shaping properties to the load(options) method. This method allows you to process these properties in any required order.
Paging
DataSource loads items by pages. The pageSize configuration property determines the page size.
- const dataSource = new DevExpress.data.DataSource({
- pageSize: 30,
- . . .
- });
Access and change the current page index with the pageIndex() and pageIndex(newIndex) methods.
- dataSource.pageIndex(1); // Switch to the next page
- dataSource.load();
To disable paging, assign false to the paginate configuration property of the DataSource.
- const dataSource = new DevExpress.data.DataSource({
- paginate: false,
- . . .
- });
Sorting
DataSources and Stores use sort expressions to specify sorting conditions and direction.
For example, declare a Person class:
- const Person = function(firstName, lastName, city) {
- this.firstName = firstName;
- this.lastName = lastName;
- this.address = {
- city: city
- };
- };
- Person.prototype.fullName = function() {
- return this.firstName + " " + this.lastName;
- };
Declare a data array with multiple instances of the Person class:
- const data = [
- new Person("John", "Smith", "San Francisco"),
- new Person("John", "Doe", "New York"),
- new Person("Xavier", "Gomez", "Denver"),
- new Person("Xavier", "Lee", "New Mexico")
- ];
Create DataSource:
- const dataSource = new DevExpress.data.DataSource(data);
Specify the sorting condition with the sort configuration property or the sort(sortExpr) method of the DataSource. You can pass a sorting expression to the property/method in one of the following formats:
A single expression
JavaScript- dataSource.sort("lastName");
- dataSource.load().done(function(result) {
- // 'result' contains the 'data' array items sorted by 'lastName'
- });
To sort data in descending order, pass an object containing the getter (field or selector - they are equivalents) and desc properties to the sort method:
JavaScript- dataSource.sort({ getter: "lastName", desc: true });
- dataSource.load().done(function(result) {
- // 'result' contains the 'data' array items sorted by 'lastName' in descending order
- });
Pass a function to the method if you need to sort by custom values.
JavaScript- dataSource.sort(function(e) {
- if(e.Position == "CEO")
- return "!";
- else
- return e.Position;
- });
- dataSource.load().done(function(result) {
- // 'result' contains the 'data' array where CEO's are displayed at the top
- // other positions are sorted in ascending order
- });
Several expressions
JavaScript- dataSource.sort(
- "fullName",
- { getter: "address.city", desc: true }
- );
- dataSource.load().done(function(result) {
- // 'result' contains the 'data' array items sorted by 'fullName' and then by 'address.city' in the descending order
- });
You can pass an array of expressions instead of using multiple arguments.
JavaScript- dataSource.sort([
- "firstName",
- "lastName",
- { getter: "address.city", desc: true }
- ]);
- dataSource.load().done(function(result) {
- // 'result' contains the 'data' array items sorted by 'firstName', then by 'lastName',
- // and then by 'address.city' in the descending order
- });
Stores support the same sort expression syntax as the DataSource.
- const arrayStore = new DevExpress.data.ArrayStore(data);
- arrayStore
- .load({
- sort: [
- { getter: "firstName", desc: true },
- "lastName"
- ]
- })
- .done(function (result) {
- // 'result' contains the 'data' array items sorted by 'firstName' in the descending order and then by 'lastName'
- });
Filtering
This section explains filter creation syntax.
Create a sample DataSource:
- const dataSource = new DevExpress.data.DataSource([
- { name: "First item", value: 5 },
- { name: "Second item", value: 7 },
- { name: "Last item", value: 3 }
- ]);
You can use a filter expression to specify filtering conditions (a set of filter expressions separated by group operators). Use the filter configuration property or the DataSource filter(filterExpr) method to define filtering conditions.
Binary Filter Operations
To specify a binary filter operation, use an array containing the following items:
- The getter that filters data items.
- The comparison operator. The following options are available:
- "="
- "<>"
- ">"
- ">="
- "<"
- "<="
- "startswith"
- "endswith"
- "contains"
- "notcontains"
- The comparison value.
Call the filter(filterExpr) method of the DataSource to specify filtering conditions. For a single binary operation in the filter expression, pass the items as an array or as separate arguments.
- dataSource.filter("value", ">", 3);
- dataSource.load().done(function(result) {
- //'result' contains the "First item" and "Second item" items
- });
- dataSource.filter("name", "contains", "st");
- dataSource.load().done(function(result) {
- //'result' contains the "First item" and "Last item" items
- });
The second item of the filter expression array is optional. You can specify only the getter and the value. The default operator is "=".
- // The following filter expressions are equal
- dataSource.filter("value", "=", 3);
- dataSource.filter("value", 3);
Unary Filter Operation
The unary not operation filters out data that matches specific conditions.
To specify an unary filter operation, use an array containing the following items.
- The "!" operator.
- The array with matching conditions.
To specify filtering conditions, call the filter(filterExpr) method of the DataSource. Note that filter expression should be included into square brackets.
- dataSource.filter( ["!", ["value", "=", 3]] );
- dataSource.load().done(function(result) {
- //'result' contains the "First item" and "Second item" items
- });
- dataSource.filter( ["!", ["name", "contains", "st"]] );
- dataSource.load().done(function(result) {
- //'result' contains the "Second item"
- });
Group Filter Operations
To combine multiple binary operations, use group operators "and" or "or".
- dataSource.filter([
- [ "value", ">", 3 ],
- "and",
- [ "value", "<", 7 ]
- ]);
- dataSource.load().done(function(result) {
- //'result' contains the "First item" item
- });
- dataSource.filter([
- [ "value", "<", 4 ],
- "or",
- [ "value", ">", 6 ]
- ]);
- dataSource.load().done(function(result) {
- //'result' contains the "Second item" and "Last item" items
- });
If neighboring filter expressions are not separated by a group operator, the "and" operator is implied.
- The following filter conditions are equal
- dataSource.filter([
- [ "value", ">", 3 ],
- "and",
- [ "value", "<", 7 ]
- ]);
- dataSource.filter([
- [ "value", ">", 3 ],
- [ "value", "<", 7 ]
- ]);
The operator priority depends on the implementation of the underlying Store. Alternatively, enclose the required expression in square brackets to define the operator priority:
- dataSource.filter([
- ["name", "notcontains", "Second"],
- "and", //calculated second
- [
- [ "value", "<", 4 ],
- "or", //calculated first
- [ "value", ">", 6 ]
- ]
- ]);
Custom Filter Conditions
A filter expression can also be a function that takes an item object and returns a Boolean value that determines whether the item meets the filter condition.
- dataSource.filter(function(itemData) {
- return itemData.value > 3 && itemData.name != "Second item";
- });
- dataSource.load().done(function(result) {
- //'result' contains the "First item" item
- });
Stores support the same filter expression syntax as the DataSource.
- const arrayStore = new DevExpress.data.ArrayStore(data);
- arrayStore
- .load({
- filter: [
- [ "value", "<", 5 ],
- "or",
- [ "value", ">", 5 ]
- ]
- })
- .done(function (result) {
- // 'result' contains the "Second item" and "Last item" items
- });
Search Api
The DataSource includes search capabilities alongside filtering. The search API consists of three properties: searchExpr, searchOperation, and searchValue.
- const dataSource = new DevExpress.data.DataSource({
- store: [
- { firstName: "John", lastName: "Doe", birthYear: 1970 },
- . . .
- ],
- filter: [ "birthYear" , "<", 2000 ],
- searchExpr: function(dataItem) {
- return dataItem.firstName + " " + dataItem.lastName;
- }
- });
- dataSource.searchValue("doe");
- dataSource.load().then(function(result) {
- //'result' contains items whose birthYear is less then 2000 and firstName or lastName contain "doe"
- });
The default search operation is "contains". To change the search operation, specify the DataSource searchOperation property. You can also modify the search API properties with the following methods:
Data Transformation
The DataSource offers multiple methods to customize the resulting array structure. You can modify each item or process the entire array.
Select Expressions
The DataSource supports the select property. This property determines the structure of items in the loaded array. When loading data, the DataSource sends the select value to the underlying Store. If the Store supports it, data transformation occurs on the server side.
Create an array of sample data:
- const data = [
- {
- firstName: "John",
- lastName: "Smith",
- city: "San Francisco"
- },
- {
- firstName: "Xavier",
- lastName: "Lee",
- city: "New York"
- },
- {
- firstName: "Maria",
- lastName: "Gomez",
- city: "Denver"
- }
- ];
You can define the select property value within the configuration object passed to the DataSource constructor.
- const dataSource = new DevExpress.data.DataSource({
- store: data,
- select: "lastName"
- });
To modify the select property, pass the new property value to the select(expr) method of the DataSource.
- dataSource.select("firstName", "city");
- dataSource.load();
A selection expression can be a getter, multiple getters, or a function.
A single getter
JavaScript- dataSource.select("lastName");
- dataSource.load().done(function(result) {
- //'result' contains the resulting array
- });
The resulting array looks like the following:
- [
- { lastName: "Smith" },
- { lastName: "Lee" },
- { lastName: "Gomez" },
- ]
Multiple getters
JavaScript- dataSource.select("firstName", "lastName");
- dataSource.load().done(function(result) {
- //'result' contains the resulting array
- });
The resulting array looks like the following:
- [
- { firstName: "John", lastName: "Smith" },
- { firstName: "Xavier", lastName: "Lee" },
- { firstName: "Maria", lastName: "Gomez" },
- ]
You can pass an array of getters instead of using multiple arguments.
JavaScript- dataSource.select(["firstName", "lastName"]);
A function
A select expression can be a function that takes an item object and returns the transformed object.
JavaScript- dataSource.select(function(dataItem) {
- return {
- fullName: dataItem.firstName + " " + dataItem.lastName,
- address: dataItem.city
- };
- });
- dataSource.load().done(function(result) {
- //'result' contains the resulting array
- });
The resulting array looks like the following:
- [
- { fullName: "John Smith", address: "San Francisco" }
- { fullName: "Xavier Lee", address: "New York" }
- { fullName: "Maria Gomez", address: "Denver" }
- ]
Stores support the same select expression syntax as the DataSource.
- const arrayStore = new DevExpress.data.ArrayStore(data);
- arrayStore
- .load({
- select: [ "firstName", "lastName" ]
- })
- .done(function (result) {
- //'result' contains the resulting array
- });
You can perform additional data transformation with the map and postProcess configuration properties of the DataSource.
Item Mapping
The map configuration property of the DataSource allows you to modify each item of the loaded array. A function passed to this property takes an initial item as a parameter and returns the processed item. This function is performed within the DataSource and is not passed to the underlying Store.
Create a sample array:
- const data = [
- {
- firstName: "John",
- lastName: "Smith",
- city: "San Francisco"
- },
- {
- firstName: "Xavier",
- lastName: "Lee",
- city: "New York"
- },
- {
- firstName: "Maria",
- lastName: "Gomez",
- city: "Denver"
- }
- ];
Assign the required function to the map configuration property of the DataSource.
- const dataSource = new DevExpress.data.DataSource({
- store: data,
- map: function(itemData) {
- return {
- fullName: itemData.firstName + " " + itemData.lastName,
- address: {
- city: itemData.city
- }
- }
- }
- });
The dataSource in the example above converts the initial data items into the following:
- [
- {
- fullName: "John Smith",
- address: {
- city: "San Francisco"
- }
- },
- {
- fullName: "Xavier Lee",
- address: {
- city: "New York"
- }
- },
- {
- fullName: "Maria Gomez",
- address: {
- city: "Denver"
- }
- }
- ]
Post Processing
To process loaded data beyond simple sorting, grouping, filtering, or data item transformation, use the DataSource postProcess property. For instance, you may need to group items by creation date (Today, This week, Long time ago). Assign a function that implements the required post processing algorithm to postProcess. The function should take the loaded array as a parameter and return the processed array.
The following code snippet uses the postProcess property to perform the task mentioned above.
- const data = [
- {
- subject: "First message",
- message: "This message has been recieved at 12/09/2013",
- date: new Date(2013, 08, 11)
- },
- {
- subject: "Today message",
- message: "This message has been recieved today",
- date: new Date()
- },
- ...
- ];
Create a DataSource instance and assign the required function to the postProcess configuration property.
- function groupByDate(data) {
- const MS_PER_DAY = 24 * 60 * 60 * 1000,
- result = [],
- todayItems = [],
- thisWeekItems = [],
- otherItems = [];
- // Fill intervals
- $.each(data, function() {
- const daysAgo = ($.now() - this.date.getTime()) / MS_PER_DAY;
- if(daysAgo < 1)
- todayItems.push(this);
- else if(daysAgo < 7)
- thisWeekItems.push(this);
- else
- otherItems.push(this);
- });
- // Construct final result
- if(todayItems.length)
- result.push({ key: "Today", items: todayItems });
- if(thisWeekItems.length)
- result.push({ key: "This week", items: thisWeekItems });
- if(otherItems.length)
- result.push({ key: "Long time ago", items: otherItems });
- return result;
- }
- const dataSource = new DevExpress.data.DataSource({
- store: data,
- postProcess: groupByDate
- });
The DataSource in the example above groups the items by the time period. The resulting array will look like the following.
- [
- {
- key: "Today",
- items: [
- //today items
- ]
- },
- {
- key: "This week",
- items: [
- //this week items
- ]
- },
- {
- key: "Long time ago",
- items: [
- //items elder then a week
- ]
- }
- ]
Grouping
In some cases, you may need to group data by certain criteria. Each item in a grouped array is an object containing two fields:
- key - a group key;
- items - an array of items in the group.
- const groupedArray = [
- {
- key: "A",
- items: [ "Amelia", "Andrew"]
- }
- {
- key: "B",
- items: [ "Betty", "Benjamin"]
- }
- ];
The DevExtreme data layer (DataSource and Stores) supports grouping. The group expression syntax is identical to the sort expression syntax.
For instance, create a sample DataSource:
- const data = [
- { name: "Amelia", birthYear: 1991, gender: "female" },
- { name: "Benjamin", birthYear: 1983, gender: "male" },
- { name: "Andrew", birthYear: 1991, gender: "male" },
- { name: "Danielle", birthYear: 1983, gender: "female" },
- { name: "Lee", birthYear: 1983, gender: "male" },
- { name: "Betty", birthYear: 1983, gender: "female" }
- ];
- const dataSource = new DevExpress.data.DataSource(data);
The group expression is stored in the group property of the DataSource. You can call the group() and group(groupExpr) methods to get and modify the group value. To group the array by "birthYear", call the group(groupExpr) method and pass "birthYear" as an argument.
- dataSource.group("birthYear");
- dataSource.load().done(function(result) {
- //'result' contains the loaded array
- });
The return value of the function that processes the loaded data contains the following array:
- [
- {
- key: 1983,
- items: [
- { name: "Benjamin", birthYear: 1983, gender: "male" },
- { name: "Danielle", birthYear: 1983, gender: "female" },
- { name: "Lee", birthYear: 1983, gender: "male" },
- { name: "Betty", birthYear: 1983, gender: "female" }
- ]
- },
- {
- key: 1991,
- items: [
- { name: "Amelia", birthYear: 1991, gender: "female" },
- { name: "Andrew", birthYear: 1991, gender: "male" }
- ]
- }
- ]
To group DataSource by custom criteria, pass a function to the group(groupExpr) method.
- dataSource.group(function(dataItem) {
- return dataItem.birthYear < 1990 ? "Born before 1990" : "Born after 1990";
- });
- dataSource.load().done(function(result) {
- //'result' contains the loaded array
- });
In this case, the 'result' array includes the following items:
- [
- {
- key: "Born after 1990",
- items: [
- { name: "Amelia", birthYear: 1991, gender: "female" },
- { name: "Andrew", birthYear: 1991, gender: "male" }
- ]
- },
- {
- key: "Born before 1990",
- items: [
- { name: "Benjamin", birthYear: 1983, gender: "male" },
- { name: "Danielle", birthYear: 1983, gender: "female" },
- { name: "Lee", birthYear: 1983, gender: "male" },
- { name: "Betty", birthYear: 1983, gender: "female" }
- ]
- }
- ]
Multi-level grouping is also supported. Pass several getters or an array of getters to the group(groupExpr) method to load an array containing subgroups.
- dataSource.group("birthYear", "gender");
- dataSource.load().done(function(result) {
- //'result' contains the loaded array
- });
The loaded array looks like the following.
- [
- {
- key: 1983,
- items: [
- {
- key: "female",
- items:[
- { name: "Danielle", birthYear: 1983, gender: "female" },
- { name: "Betty", birthYear: 1983, gender: "female" }
- ]
- },
- {
- key: "male",
- items:[
- { name: "Benjamin", birthYear: 1983, gender: "male" },
- { name: "Lee", birthYear: 1983, gender: "male" }
- ]
- }
- ]
- },
- {
- key: 1991,
- items: [
- {
- key: "female",
- items:[
- { name: "Amelia", birthYear: 1991, gender: "female" }
- ]
- },
- {
- key: "male",
- items:[
- { name: "Andrew", birthYear: 1991, gender: "male" }
- ]
- }
- ]
- }
- ]
Stores support the same group expression syntax as the DataSource.
- const arrayStore = new DevExpress.data.ArrayStore(data);
- arrayStore
- .load({
- group: "birthYear"
- })
- .done(function (result) {
- // 'result' contains the 'data' array grouped by the 'birthYear' property value.
- });
Data Modification
To enable read-write access to data, directly connect to a Store in addition to DataSource.
To obtain a Store instance, do one of the following:
Create a Store instance explicitly.
JavaScript- const store = new DevExpress.data.ArrayStore({
- key: "id",
- data: [
- {
- id: 1,
- value: "value 1"
- },
- {
- id: 2,
- value: "value 2"
- }
- ]
- });
NOTESpecify the key expression to identify data items.To load data from the Store, call store.load(loadOptions) or wrap the Store with the DataSource.
To obtain the underlying Store instance from an existing DataSource, use the store() method.
Insertion
To insert a new item into the Store, call the store.insert(values) method.
- store.insert({
- id: 3,
- value: "value 3"
- })
- .done(function(values, key) {
- //handle success
- })
- .fail(function(error) {
- //handle error
- });
Some Stores can generate a key value if it is not specified in the values object. Access it through the key argument of the done callback.
Update
To update a data item specified by the key, use the store.update(key, values) method.
- store.update(1, { value: "new value" })
- .done(function(values) {
- //handle successful updating
- })
- .fail(function(error) {
- //handle error
- });
Before updating, use the store.byKey(key) method to load the required item.
Removing
To remove a data item, call the store.remove(key) method.
- store.remove(1)
- .done(function(key) {
- //handle success
- })
- .fail(function(error) {
- //handle error
- });
Integration with Push Services
Integration with push services allows the store to notify the DataSource and its bound UI components about data changes. After receiving the notification, the DataSource can reapply data processing operations, and the components can refresh the UI in real time.
All stores include the push(changes) method used to insert, update, and remove data objects. The method accepts an array, which allows you to update data in batches.
- store.push([{ type: "insert", data: data }]);
- store.push([{ type: "update", data: data, key: key }]);
- store.push([
- { type: "remove", key: key1 },
- { type: "remove", key: key2 }
- ]);
Call this method in the event handlers for client functions that can be invoked from the server.
DataGrid SignalR Demo Scheduler SignalR Demo Chart SignalR Demo
After being notified, the DataSource can reapply sorting, filtering, grouping, and other data processing settings if you set the reshapeOnPush property to true. If pushes come too frequently, specify pushAggregationTimeout to aggregate them.
Events and Change Tracking
DataSource and Stores support events. The DataSource events are used to track the data loading state. They are used internally by the DevExtreme UI components. You can also use them for your purposes. Stores also raise events before and after each operation (loading, update, insertion, etc.).
Use one of the following approaches to handle events.
Assign a Handler to a Configuration Property
To attach a handler for a certain event to a DataSource or Data Store, use the corresponding configuration property. The properties that take on event handling functions have names starting with on.
The following example demonstrates how to use a configuration property to handle an event.
- var dataSource = new DevExpress.data.DataSource({
- sort: "name",
- pageSize: 10,
- store: store,
- onChanged: function() {
- // Executed after the DataSource has successfully loaded data
- }
- });
Attach Several Handlers Using one Method
To attach several event handlers to a DataSource or Data Store, use the on method. This method has two overloads.
on(eventName, eventHandler)
This method allows you to provide several handlers for an event.JavaScript- dataSource
- .on("changed", handler1)
- .on("changed", handler2)
on(events)
This method allows you to subscribe to several events with one method call.JavaScript- dataSource
- .on({
- "changed": handler1,
- "loadError": handler2
- })
You can find a full list of events in the Events section: DataSource events and Store events (for example, ArrayStore events).
To unsubscribe from the events that you handled using the on() method, use the off() method. This method has two overloads.
off(eventName, eventHandler)
This method allows you to detach a specific event handler.JavaScript- dataSource
- .off("changed", handler1)
- .off("changed", handler2)
off(eventName)
This method allows you to detach all event handlers from a specific event.JavaScript- dataSource
- .off("changed")
Handling Errors
DevExtreme includes the setErrorHandler utility method to help you handle errors that occur within the entire data layer. Pass an error handler to this method. The handler accepts a JavaScript Error object as a parameter.
- import { setErrorHandler } from "data/errors";
- setErrorHandler(function(error) {
- console.log(error.message);
- });
A Store enables you to handle errors that occur only within itself. To handle Store errors, assign an error handling function to the handleError configuration property of the Store.
- var store = new DevExpress.data.LocalStore({
- name: "MyStore",
- errorHandler: function(error) {
- console.log(error.message);
- }
- };
DevExtreme also provides an ability to handle errors that occur during operation execution. All DevExtreme data transfer operations are asynchronous and return a Promise (a native Promise) allowing you to specify both success and error callbacks.
Query Concept
Besides DataSource and Stores, DevExtreme data layer has one more useful concept named Query. Query is an abstract chainable interface which provides functionality to evaluate data queries.
There are several implementations of this interface, and they are used internally in some Stores as part of the load() method. While Query is considered internal instrument, one implementation, which is a wrapper over a JavaScript array, is particularly interesting, and can be used in the application code.
Here is an example:
- var processedArray = DevExpress.data.query(inputArray)
- .filter([ [ "value", ">=", 10 ], "and", [ "value", "<=", 90 ]])
- .sortBy("lastName")
- .thenBy("firstName")
- .select("lastName", "firstName", "value")
- .toArray();
The full list of supported methods is given in the Query API reference.
Getters And Setters
One of the basic data layer concepts is getters and setters. Getters are essential tools used to specify sorting, grouping, filtering, and data transformation rules.
A getter function returns a value of a predefined property of an object passed to the getter as a parameter.
A setter function assigns a new value to the predefined property of an object passed to the setter as a parameter. The new value is specified through the second parameter of a setter.
For example, create a sample object:
- var person = {
- firstName: "John",
- lastName: "Smith",
- birthDate: new Date(1985,10,15),
- getBirthYear: function(){
- return this.birthDate.getFullYear();
- },
- address: {
- zipCode: 90007,
- state: "CA",
- city: "Los Angeles",
- getAddress: function(){
- return this.zipCode + " " + this.state + " " + this.city;
- }
- }
- };
Creating Getters And Setters
DevExtreme includes methods used to create a getter and setter for the property specified as a string.
- const getter = DevExpress.data.utils.compileGetter("firstName");
- const setter = DevExpress.data.utils.compileSetter("firstName");
To get a value of the person.firstName property, call the created getter and pass the person object to it.
- // the getter returns "John" and assigns it to the 'name' variable
- const name = getter(person);
To assign a new value to the person.firstName property, call the created setter and pass the person object and the new value to it.
- // assigns "Michael" to 'person.firstName'
- setter(person, "Michael");
Specify the property for creating a getter or setter as a string passed to the compileGetter or compileSetter method. The property string can be in one of the following formats:
A property name
Specifies a top-level property.JavaScript- const getter = DevExpress.data.utils.compileGetter("firstName");
- const setter = DevExpress.data.utils.compileSetter("firstName");
- // the getter returns "John" and assigns it to the 'name' variable
- const name = getter(person);
- // assigns "Michael" to 'person.firstName'
- setter(person, "Michael");
A second and higher level property
Specifies a second and higher level property.JavaScript- const getter = DevExpress.data.utils.compileGetter("address.city");
- const setter = DevExpress.data.utils.compileSetter("address.city");
- // the getter returns "Los Angeles" and assigns it to the 'city' variable
- const city = getter(person);
- // assigns "San Francisco" to 'person.address.city'
- setter(person, "San Francisco");
A method name
Specifies the method whose value should be returned by the getter.JavaScript- const getter = DevExpress.data.utils.compileGetter("getBirthYear");
- // the getter returns 1985 and assigns it to the 'year' variable
- const year = getter(person);
An array of property and method names
If you pass an array of strings to the compileGetter function, the compiled getter will return an object with the specified properties. For second or higher level properties in the array, the getter creates the necessary subobjects within the returned object.JavaScript- const getter = DevExpress.data.utils.compileGetter(["firstName", "lastName", "address.city", "getBirthYear"]);
- const personData = getter(person);
The specified getter called for the person object will return the following object.
- {
- firstName: "John",
- lastName: "Smith",
- getBirthYear: 1985,
- address: {
- city: "Los Angeles"
- }
- }
Note that the returned object contains the getBirthYear property that holds the value returned by the method with the same name.
A function
If you pass a function to compileGetter, this function will be executed each time the getter is called. The function should have a parameter that takes on an object passed to the getter.JavaScript- const getter = DevExpress.data.utils.compileGetter(function(dataItem) {
- return dataItem.firstName + " " + dataItem.lastName;
- });
- // the getter returns "John Smith" and assigns it to the 'fullName' variable
- const fullName = getter(person);
"this"
In this case, the getter returns the object itself. This getter is used to sort or group an array of items containing a simple value.JavaScript- const getter = DevExpress.data.utils.compileGetter("this");
- // the getter returns "A text" and assigns it to the 'text' variable
- const text = getter("A text");
Getter And Setter Calling Options
If you assign a method name to compileGetter, the getter returns this method's return value. To get a reference to the method instead of its value, pass an object with functionAsIs: true
to the getter as a second parameter.
- const getter = DevExpress.data.utils.compileGetter("address.getAddress");
- // the getter returns a reference to the 'person.address.getAddress' function and assigns it to the 'getAddressFunction' variable
- const getAddressFunction = getter(person, { functionsAsIs: true });
If the getter returns an object containing multiple functions, the functionsAsIs property affects all functions contained in this object.
- const setter = DevExpress.data.utils.compileSetter("address");
- setter(person, {
- city: "San Francisco",
- street: "Stanford Ave"
- });
In the example above, the object held in the person.address property will contain only the city and street properties after the setter will be called. Other members will be lost.
If you need to merge the new object passed to the setter with the object held in the property, pass a properties object containing merge: true
to the setter.
- const setter = DevExpress.data.utils.compileSetter("address");
- setter(
- person,
- {
- city: "San Francisco",
- street: "Stanford Ave"
- },
- { merge: true }
- );
In this case, the address object contains the following properties:
- {
- zipCode: 90007,
- state: "CA",
- city: "San Francisco",
- street: "Stanford Ave",
- getAddress: function() {
- return this.zipCode + " " + this.state + " " + this.city;
- }
- }
If you have technical questions, please create a support ticket in the DevExpress Support Center.