# Cumulocity Data-Grid Tutorial Series - Part 3

**URL:** <https://community.cumulocity.com/t/cumulocity-data-grid-tutorial-series-part-3/3495>\
**Category:** Knowledge Base\
**Tags:** tutorial, cumulocity\
**Created:** [March 14, 2023, 3:14pm UTC](https://community.cumulocity.com/t/cumulocity-data-grid-tutorial-series-part-3/3495 "2023-03-14T15:14:37Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![Hendrik\_Naether1](https://dub1.discourse-cdn.com/flex005/user_avatar/community.cumulocity.com/hendrik_naether1/32/17494_2.png) [@Hendrik\_Naether1](https://community.cumulocity.com/u/Hendrik_Naether1)\
**Post date:** [March 14, 2023, 3:14pm UTC](https://community.cumulocity.com/t/cumulocity-data-grid-tutorial-series-part-3/3495/1 "2023-03-14T15:14:37Z")

</div>

## The remote-data approach, or demystifying the `serverSideDataCallback`

As pointed out in [chapter 1](https://community.cumulocity.com/t/cumulocity-data-grid-tutorial-series-part-1/7211) and [chapter 2](https://community.cumulocity.com/t/cumulocity-data-grid-tutorial-series-part-2/6019) of the `c8y-data-grid` tutorial series, the easiest and potentially dirtiest way to set up a grid is to use the `rows` attribute. As this doesn’t scale at all if all the data needs to be fetched and held on the client side, we want to use the remote-data approach by setting up a `serverSideDataCallback` and generating queries whenever the user triggers actions on the grid.

The secret sauce that will drastically improve the performance is the combination of using _queries **and** pagination_. That way we will always show data matching the user’s sort and filter criteria, but drastically reduced to just a small dataset being fetched and shown.

## The Challenge

The server-side data “mode” requires a lot more effort to be set up and knowledge in many different areas as you need to understand:

- the data source and as such the API you want to query
- the query parameters and/or query language that this API offers
- the abstraction layer of @c8y/client  
o how to setup filter objects for list queries  
o how to setup query json which is then parsed to a query string by `QueriesUtil`
- how to create own custom filter view components  
o how to persist a query in the column  
o how to read and translate the query back to be reflected in the filter UI

## The Plan

![it-begins](https://europe1.discourse-cdn.com/flex005/uploads/cumulocity/original/3X/4/4/444c83f25487a140de637d2b3961a57303d887ec.gif)

In the following example, we are going to display a list of devices (by using the [Inventory API](https://cumulocity.com/api/core/#operation/getManagedObjectCollectionResource)). This implies that we target a Cumulocity IoT Tenant with our queries and can use the [query language](https://cumulocity.com/api/#tag/Query-language) to our advantage.

You can already check out the example of the [remote-data-example](https://github.com/Cumulocity-IoT/cumulocity-data-grid-samples/tree/main/src/remote-data-example) from the GitHub repository and follow along while reading the code there.

Our tasks are as follows:

1. Wire up the `serverSideDataCallback` property
2. Create queries based on the grid state

## Wire up the `serverSideDataCallback` property

![wire-up](https://europe1.discourse-cdn.com/flex005/uploads/cumulocity/original/3X/e/9/e9ef939109e6944624c7e266492a9c097af550b3.gif)

The first task you need to do is to wire up a method that is being bound to the `serverSideDataCallback` property of the grid. It is recommended to move the logic of that to its own service so that you can reuse that service for different c8y-data-grids.

 ![Bildschirm­foto 2023-02-17 um 13.39.39](https://europe1.discourse-cdn.com/flex005/uploads/cumulocity/original/3X/e/4/e43329051e32576e51456dbf68a0ce01fa82a16c.png)  
Inside of your components html ([Example](https://github.com/Cumulocity-IoT/cumulocity-data-grid-samples/blob/main/src/remote-data-example/remote-data-grid.component.html))

 ![Bildschirm­foto 2023-02-17 um 13.40.15](https://europe1.discourse-cdn.com/flex005/uploads/cumulocity/original/3X/2/6/266943a877b3cf2273455810c7049df165373f7e.png)  
Inside of the service you’re wiring up ([Example](https://github.com/Cumulocity-IoT/cumulocity-data-grid-samples/blob/main/src/services/devices-datasource.service.ts))

By using the `bind`-method, we can bind a method call as if it was a property binding. Make sure to provide and inject that service into your component containing the `c8y-data-grid`. Now that the `serverSideDataCallback` is wired up, the method `onDataSourceModifier` is called, whenever the grid needs to reload. This happens whenever:

- the grid loads for the first time
- the user (re-)sets a filter or sortation
- the user clicks the reload button
- the user changes the current page

The `datasourceModifier` parameter will then contain the current state of the grid, such as:

- current state of all columns (whether filter or sortation is set)
- searchText (if `showSearch` was set to `true` on the grid and the user entered text or cleared the search-field)
- pagination information

We will now need to generate queries based on that information. This task will be taken care of by the [InventoryDatasourceService](https://github.com/Cumulocity-IoT/cumulocity-data-grid-samples/blob/479bf7573a6d59b8b5e70cc0f7915aa0529d683e/src/services/inventory-datasource.service.ts) on which we call `reload` (see line 24 of the previous code-example).

As we intend to always just fetch devices, meaning Managed Objects with the `c8y_IsDevice` fragment, we have a base filter that always needs to be applied. We pass the state of the grid and this base query to the `reload` method of the `InventoryDataSourceService`.

These two services are decoupled by intention, as that way you could also create a `GroupDataSourceService` having the same `InventoryDataSourceService` as its foundation, just passing a different base-query.

## Create queries based on the grid state

Based on the information from the `datasourceModifier` parameter, we will now need to create 3 queries:

 ![Bildschirm­foto 2023-02-17 um 13.46.40](https://europe1.discourse-cdn.com/flex005/uploads/cumulocity/original/3X/2/8/28e5b9ef929da41af1b6e421b90f4282e0c1fd18.png)

Usually, just one query would be sufficient, which is the query where all filters etc. are considered and the data of the current page would be queried and displayed. Unless you configure the grid to hide its header (by setting the `gridHeader` property of the grid’s `displayOptions` to false) and disable pagination (by configuring a [load more mode](https://github.com/Cumulocity-IoT/cumulocity-data-grid-samples/blob/main/src/customization-example/customization-grid.component.ts)) the grid will show how many items match your current filter/ search and an overall count of items if no filter/ search was applied. This information must be queried too, adding 2 more queries as a result.

We will now have a look at how to implement these 3 queries by starting from the top of the call hierarchy and going down more and more into the details of how this is implemented.

### The `reload` method

![reload](https://europe1.discourse-cdn.com/flex005/uploads/cumulocity/original/3X/8/1/81cbc27a2fad6223cf6d973a5302cb93bb53a521.gif)

Have a look at the implemented reload method:

 ![Bildschirm­foto 2023-02-17 um 13.54.38](https://europe1.discourse-cdn.com/flex005/uploads/cumulocity/original/3X/e/a/ead2028d1ae4a9ff2f947a1afe0ac4afe3827318.png)  
([Example](https://github.com/Cumulocity-IoT/cumulocity-data-grid-samples/blob/479bf7573a6d59b8b5e70cc0f7915aa0529d683e/src/services/inventory-datasource.service.ts))

You can see these 3 queries being declared on lines 21 to 23:

- actual dataset matching the filters (line 21)
- count of dataset matching the filters (line 22)
- count of the dataset without filters (line 23)

Before sending the queries, we first need to create the query strings (lines 18-19). Inside of the fetch calls, these query strings are added as `query`-attribute to the filter object that is passed to the `list`-method of the `InventoryService`. One query (here `filterQuery` in line 18) will contain the actual query information, while the other query just contains the base query (`allQuery` in line 19). The results of the queries are then returned combined as `ServerSideDataResult` which the grid will then consume to update its view.

Next, let’s have a detailed look at the methods `fetchManagedObjectsForPage` and `fetchManagedObjectsCount` which are called by the `reload` method.

### The fetch methods: `fetchManagedObjectsForPage` and `fetchManagedObjectsCount`

#### `fetchManagedObjectsForPage`

 ![Bildschirm­foto 2023-02-17 um 13.56.30](https://europe1.discourse-cdn.com/flex005/uploads/cumulocity/original/3X/b/0/b0178f893faad5d4e121dd75d79fb5d3ae9d707f.png)  
([Example](https://github.com/Cumulocity-IoT/cumulocity-data-grid-samples/blob/479bf7573a6d59b8b5e70cc0f7915aa0529d683e/src/services/base-inventory-datasource.service.ts))

In the method `fetchManagedObjectsForPage` we just combine the query string, pagination information (`pageSize` and `currentPage` which are [destructured](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment?retiredLocale=en) from paging) to a filter object that we then pass to the `list` method.

If you want more information about parent-managed objects, you can set `withParents` to `true`. It’s important to set `withTotalPages` to `false` as the number of pages is calculated via the `fetchManagedObjectsCount` calls. Unnecessarily setting `withTotalPages` to `true` would drain the performance.

The `list` method then retrieves and returns _all items matching the query parameter for the current page_, cut into little chunks depending on the size of the `pageSize` attribute that was configured in the `filter` object.

#### `fetchManagedObjectCount`

 ![Bildschirm­foto 2023-02-17 um 13.56.45](https://europe1.discourse-cdn.com/flex005/uploads/cumulocity/original/3X/a/b/ab35f9c6fd906112f629523dd4c254a373f24bdf.png)  
([Example](https://github.com/Cumulocity-IoT/cumulocity-data-grid-samples/blob/479bf7573a6d59b8b5e70cc0f7915aa0529d683e/src/services/base-inventory-datasource.service.ts))

The method `fetchManagedObjectCount` doesn’t use a count endpoint of the API, but instead also uses the `list` method, but with adds a little trick: By setting the `pageSize` to `1` and `withTotalPages` to `true`, the `totalPages` information of the retrieved response matches the count of items in our dataset. The method returns this count wrapped in a Promise (lines 59-61).

### Query generation

Now that we have covered how the requests are sent, let’s now dive even deeper and check out how the queries for these requests are generated. Remember – the `filterQuery` and `allQuery` strings were the first things we created in the `reload` method of the `InventoryDataSourceService`.

#### The `createQueryFilter` function

 ![Bildschirm­foto 2023-02-17 um 13.58.31](https://europe1.discourse-cdn.com/flex005/uploads/cumulocity/original/3X/9/3/93da70bb9fe8f7ff97507c81d8e1fc3084157e61.png)  
([Example](https://github.com/Cumulocity-IoT/cumulocity-data-grid-samples/blob/479bf7573a6d59b8b5e70cc0f7915aa0529d683e/src/services/base-inventory-datasource.service.ts#L65))

The function `createQueryFilter` generates a query string based on the information it gets from the `columns`  
-array and the `baseQuery` parameter. If you want to extend your query also by the search, you could also add it as a third parameter here (by extracting it from the `datasourceModifier` parameter of the `reload` method).

The approach is to create a query JSON object which then the [QueriesUtil](http://resources.cumulocity.com/documentation/websdk/client/classes/queriesutil.html) (which is part of @c8y/client) converts to a string. You can find examples of how such query strings look in the [OpenAPI documentation](https://cumulocity.com/api/10.15.0/#tag/Query-language). If you need help with how to build the JSON query object, hover over the `buildQuery` method and check out the comprehensive JSDoc guide there.

 ![Bildschirm­foto 2023-02-17 um 11.01.48](https://europe1.discourse-cdn.com/flex005/uploads/cumulocity/original/3X/9/a/9ae8b14868e279106dab2bb2c4db5716caf97634.png)  
Make sure to check out the comprehensive JSDoc of the `buildQuery` method ([Example](https://github.com/Cumulocity-IoT/cumulocity-data-grid-samples/blob/479bf7573a6d59b8b5e70cc0f7915aa0529d683e/src/services/base-inventory-datasource.service.ts#L23))

This query string is then used in the filter parameter that is given to the `list` function of the `InventoryService`. You could basically also set query strings directly on the `filter` object in a static way, but in this context, we need to dynamically create it, depending on the state of the columns and whether or not filters or sortations are set or not.

This dynamic part is done in the `reduce` function, which starts with a very basic JSON structure, containing the ` __filter` attribute with the base query or just an empty object. As previously mentioned – the grid supports sorting _multiple columns_ and this also reflects in the `__ orderby` attribute being initialized with an empty _array_.

#### The `extendQueryByColumn` function

 ![Bildschirm­foto 2023-02-17 um 14.30.51](https://europe1.discourse-cdn.com/flex005/uploads/cumulocity/original/3X/3/e/3e699a2729fdc17b1b59e5e098c8e7c0758df74e.png)  
([Example](https://github.com/Cumulocity-IoT/cumulocity-data-grid-samples/blob/479bf7573a6d59b8b5e70cc0f7915aa0529d683e/src/services/base-inventory-datasource.service.ts#L73))

For every column, we extend the `queryJSON` object in case filter or sortation information is available in the column.

##### \_\_filter

In case the user has set a standard string filter on a column in the UI, it would implicitly mean that the `filterable` attribute was set to `true` for that column and we can expect the `filterPredicate` to contain a string with the value that the user entered. We convert that to an _equals-query_ by setting the path as key and the `filterPredicate`-content as the value (line 86) of `__filter`.

More complex queries, and especially custom-filter queries, are usually written into the `externalFilterQuery` attribute of the column. We expect the custom filter view to already set an appropriate query JSON and just extend `__filter` with whatever is stored in the `externalFilterQuery` object.

##### \_\_orderby

Next, we need to check whether a column has been sorted or not. As columns are sortable by default, we do not check for `sortable` to be true, but instead just check if the `sortOrder` got set for that column as it changes whenever the user clicks on the sort button.

If a `sortOrder` was set, and thus the column is sorted _ascending_ or _descending_, we add a key-value pair to the `__orderby` array, where the key is the path of the column, and the value is either `1` for _ascending_ or `-1` for _descending_ order (line 94).

## Done!

![done](https://europe1.discourse-cdn.com/flex005/uploads/cumulocity/original/3X/8/6/8645d2d1483461231977a45d54b1e0b6c8eec890.gif)

That’s it for a basic setup if you want to use the remote-data “mode” of the c8y-data-grid. We have covered how to wire the grid up to a datasource-service, why to use a base-query, and how the `InventoryDataSourceService` translates the state of the grid’s columns into list queries going against the Cumulocity IoT tenant.

 ![remote-data-result](https://europe1.discourse-cdn.com/flex005/uploads/cumulocity/original/3X/0/e/0e42c62257acb1742e5b31b4c8b7a7a4c9a471cd.png)  
If you want to see the complete code working in action, [please check out the open-source project](https://github.com/Cumulocity-IoT/cumulocity-data-grid-samples), run it locally and have a look at the remote-data-example.

The result doesn’t look that different from the local data approach, right? But under the hood, we are now using a way more powerful approach that will guarantee good performance even if large datasets are shown.

---

<div class="post-metadata">

**Author:** ![davidelh](https://avatars.discourse-cdn.com/v4/letter/d/e47774/32.png) [@davidelh](https://community.cumulocity.com/u/davidelh)\
**Post date:** [March 26, 2024, 4:41pm UTC](https://community.cumulocity.com/t/cumulocity-data-grid-tutorial-series-part-3/3495/2 "2024-03-26T16:41:40Z")

</div>

Hello, thank you for this tutorial. I am currently trying to implement a grid to display all the measurements from a specific device using the remote-data approach. I managed to follow this guide (part 3) and adapt it to use the measurement service included in the github project. However I am stuck and while testing the app I keep receiving this error: **ERROR TypeError: Cannot read properties of undefined (reading ‘serverSideDataCallback’)**  
What I’d like to obtain is a widget version of the data-grid, where the user can select a device from widget configuration and the device id is used as input for the service to query measurements of that specific device. Is there a guide on how to use data-grid component as a widget?
