Hello Syncfusion Team,
I am encountering an issue while using DataManager with the Syncfusion Grid in my Angular application. My goal is to create a service that returns a DataManager, and that every action on the grid (pagination, sorting, filtering, etc.) triggers an API request via DataManager, while also adding custom content to the payload of each request.
Issue:
When trying to add a custom object via addParams() or other approaches, the internal Syncfusion parameters (like skip, take, sorted, etc.) get overwritten, causing the grid to malfunction. The following error is also generated in the console:
zone.js:1188 Uncaught TypeError: Cannot read properties of undefined (reading 'pvtData')
When using a CustomUrlAdaptor, I was also losing essential information from DataManager such as the URL and other parameters like pvtData, which caused issues with the grid functionality.
What I’ve Tried:
-
Service with UrlAdaptor and addParams():
I tried adding custom content to the payload using Query.addParams(), but it resulted in the loss of pagination, sorting, and other parameters.
Example code:
public getObjectsDataWithUrlAdaptor(objecttype: string, viewlist: string, views: string[], relations: { [key: string]: unknown } | null): DataManager {
let content: { [key: string]: unknown } = {
items: viewlist,
views,
};
if (relations) {
content = { ...content, relations };
}
const url = `${this.#apiUrl}/${BASE_URL}/urladaptor/${objecttype}`;
const dataManager = new DataManager({ url: url, adaptor: new UrlAdaptor() });
const query = new Query().addParams('content', JSON.stringify(content));
dataManager.executeQuery(query);
return dataManager;
}
However, by adding custom content via addParams(), the internal parameters (like skip, take, sorted) are lost, which prevents the grid from working correctly.
- Attempts with CustomUrlAdaptor:
I also attempted to create a CustomUrlAdaptor to customize the request process, but this resulted in the loss of the DataManager URL and other essential information such as pvtData.
Example of CustomUrlAdaptor code:
import { DataManager, Query, UrlAdaptor } from '@syncfusion/ej2-data';
export class CustomUrlAdaptor extends UrlAdaptor {
processQuery(dm: DataManager, query: Query, hierarchyFilters?: object[]): object {
const request = super.processQuery(dm, query, hierarchyFilters) as any;
const content = (dm as any).customContent || {};
const customData = {
content: content,
params: query.params || {},
};
request.data = JSON.stringify(customData);
return request;
}
}
However, when using this CustomUrlAdaptor, I was losing the DataManager URL as well as other important information like pvtData, which hindered the grid's functionality.
Desired Solution:
I want to add custom content to the payload of every request sent by the grid, while preserving Syncfusion’s internal parameters such as skip, take, sorted, etc., to ensure the grid functions correctly. I am looking for a way to achieve this while making sure that every action on the grid (pagination, sorting, filtering, etc.) triggers a valid API request.
Additional Information:
Syncfusion version used: 26.2.10
Angular version used: 18.2.0
Thank you in advance for your help.
Best regards,
DEVAUX
Hi DEVAUX,
Greetings from Syncfusion Support.
We have reviewed the provided details. In your code, you are executing the query on the DataManager instance using the executeQuery method. This immediately executes the query and sends an API request at the time of execution, rather than appending the query to the requests made by the Grid. To include additional parameters in the Grid's requests using DataManager, you need to add the query directly to the Grid’s “query” property. This approach ensures that the additional parameters are sent along with the Grid's requests. You can refer to the following documentation for more details:
Documentation: https://ej2.syncfusion.com/angular/documentation/api/grid/#query
However, based on the provided details, we understand that your requirement is to create a service that returns a DataManager instance with predefined properties. You can achieve this by creating CustomAdaptor from UrlAdaptor and overriding the processQuery method. This allows you to append custom properties to the request payload before sending it to the server without losing the default parameters.
Here is a sample implementation for your reference:
|
[data.service.ts]
public getObjectsDataWithUrlAdaptor(objecttype: string, viewlist: string, views: string[], relations: { [key: string]: unknown } | null): DataManager { let content: { [key: string]: unknown } = { items: viewlist, views, relations }; const url = `https://services.syncfusion.com/js/production/api/UrlDataSource`; const dataManager = new DataManager({ url: url, adaptor: new (class extends UrlAdaptor { processQuery() { const request: any = super.processQuery.apply(this, arguments); const requestData = JSON.parse(request.data);
requestData['content'] = content; request.data = JSON.stringify(requestData);
return request; } })(), }); return dataManager; }
|
Sample: https://stackblitz.com/edit/angular-pqeq2j-sq17fnbw
[preview]
Please let us know if you need any further assistance.
Regards,
Santhosh I
Hello Syncfusion Team,
Thank you for your response, I tried creating a CustomAdaptor in my service to add parameters as you suggested. Here is the code I used :
public getObjectsDataWithCustomAdaptor(
objecttype: string,
viewlist: IViewList,
views: string[],
relations: { [key: string]: unknown } | null,
formId: string | null = null
): DataManager {
let content: { [key: string]: unknown } = {
items: viewlist?.items,
kanban: viewlist.kanban,
queryBuilderFilter: viewlist?.queryBuilderFilter,
orderArray: viewlist?.orderArray,
views,
formId,
};
if (relations) {
content = {
...content,
relations,
};
}
const bearer = `Bearer`;
const url = `${this.#apiUrl}/${BASE_URL}/customadaptor/${objecttype}`;
const dataManager = new DataManager({
url: url,
adaptor: new (class extends UrlAdaptor {
processQuery(dm: DataManager, query: Query, hierarchyFilters?: object[]) {
console.log('DataSource 2');
console.log(dm.dataSource);
const request: any = super.processQuery.apply(this, [
dm,
query,
hierarchyFilters,
]);
const requestData = JSON.parse(request.data);
requestData['content'] = content;
request.data = JSON.stringify(requestData);
return request;
}
})(),
headers: [{ Authorization: bearer }],
});
console.log('DataSource 1');
console.log(dataManager.dataSource);
return dataManager;
}
However, i encountered the following console error :
Uncaught TypeError: Cannot read properties of undefined (reading 'pvtData')
at UrlAdaptor.processResponse (ej2-data.es5.js:3816:28)
at DataManager.executeLocal (ej2-data.es5.js:6123:29)
at ej2-data.es5.js:6171:33
at run (apps\admin\src\app\features\v1\ng1-web\libs\schemaform\angular-schema-form.js:10916:1)
at runIfPresent (apps\admin\src\app\features\v1\ng1-web\libs\schemaform\angular-schema-form.js:10942:1)
at onGlobalMessage (apps\admin\src\app\features\v1\ng1-web\libs\schemaform\angular-schema-form.js:10986:1)
This is the same issue I had previously. After i added logs to check, I noticed that the DataSource 1 was triggered first, and it contained the right URL as well as other informations. However, DataSource 2 does not contain my URL anymore :
Could you provide me a solution for this issue and explain to me what might be going wrong ?
Thank you in advance for your assistance.
Best regards,
DEVAUX Romain
Hi DEVAUX,
The issue you are currently facing (Uncaught TypeError: Cannot read properties of undefined (reading 'pvtData')) has been resolved in the DataManager component in version 27.1.52. As you are using a version prior to this release, you are encountering the issue. To resolve this, we recommend upgrading your package to the latest version or to version 27.1.52, which includes the fix. For your reference, a working sample using the latest version is provided below:
Sample: https://stackblitz.com/edit/angular-pqeq2j-sq17fnbw
Regards,
Santhosh I
Hello,
Thank you for your previous responses; they have solved most of my issues.
I have another question regarding the Excel filters for the grid columns. I would like the loading of these filters to be performed through a backend request, just like the data, using a DataManager. I have done some research and found this example:
However, in my case, I am facing an issue with this approach. Let me explain:
I have a first DataManager that retrieves data and displays it in my grid. This part is working fine now, and the code I shared earlier works well. But I want to use a second DataManager (different from the first one) to populate the Excel filters. So, I created a second method: getExcelFilterWithCustomAdaptor, which returns a DataManager, and I use it in the actionBegin event, just like in the example.
On the server side, I ensure that a SELECT DISTINCT is performed so that only the necessary data is retrieved. Even though my request returns the expected data for the filters—following the response schema from the documentation—no filters are displayed.
I am wondering: Is it possible to have two different DataManager instances in the same grid? One for the grid’s dataSource and another for the Excel filter’s dataSource.
Do you have an example of this? Would you like me to provide more details about my case?
Thank you!
Hi DEVAUX,
As explained in the documentation, you can assign a different remote data source for the Excel Filter CheckBox list using the actionBegin event. As long as the returned data is formatted correctly, it will populate the CheckBox list as expected. Please refer to the following code snippet and sample, which demonstrate how to bind a separate DataManager instance with a query that selects only the current column:
|
[app.component.ts]
actionBegin(args) { if (args.requestType === 'filterBeforeOpen') { // new remote data source for checkbox list args.filterModel.options.dataSource = this.dataService.getExcelFilterWithCustomAdaptor(); } if (args.requestType === 'filterchoicerequest' || args.requestType === 'filterSearchBegin') { // remove query for distinct value args.query.distincts = []; // select current column args.query.select(args.filterModel.options.field); } }
|
Sample: https://stackblitz.com/edit/angular-pqeq2j-66u6qtvu
If the CheckBox list displays a blank option even after the data is successfully loaded, it indicates that the field value in the returned records does not match the column’s field property. Ensure that the field in the returned data exactly matches the column field used in the Grid.
Please review the provided sample to compare the request sent and the response received, and verify that your implementation follows the same format.
Regards,
Santhosh I
Hello, and thank you for your response.
Thanks to your explanation, I was able to resolve my issue with retrieving filters. Now, for each column, the filters are correctly fetched from the back-end and displayed properly in the Excel-style filter window.
However, I've noticed a problem: if I apply a filter on column A and then open the filter dropdown on column B, the list appears empty—even though the back-end returns the correct data and I haven't yet applied the filter to the dataset, so the grid still displays the same data. If I remove the filter on column A, the filter options for column B show up correctly again.
Could you help me understand and resolve this issue?
Thank you!
Hi DEVAUX,
The behavior you are observing occurs because, by default, all previously applied filter queries are evaluated when opening the checkbox list for any column. To prevent this and ensure that only the current column’s filter is considered, you can modify the “filteredColumns” list within the filterModule to retain only the entry corresponding to the active column.
This adjustment should be made within the “actionBegin“ event handler when the requestType is "filterBeforeOpen", immediately after customizing the checkbox list's dataSource.
|
[app.component.ts]
actionBegin(args) { if (args.requestType === 'filterBeforeOpen') { // new remote data source for checkbox list args.filterModel.options.dataSource = this.dataService.getExcelFilterWithCustomAdaptor();
// remove filtered field except current column field args.filterModel.options.filteredColumns = args.filterModel.options.filteredColumns.filter( (col) => col.field === args.columnName ); } if ( args.requestType === 'filterchoicerequest' || args.requestType === 'filterSearchBegin' ) { // remove query for distinct value args.query.distincts = []; // select current column args.query.select(args.filterModel.options.field); } }
|
Sample: https://stackblitz.com/edit/angular-pqeq2j-vvwttl61
Regards,
Santhosh I
Hello Syncfusion Team,
Thank you for your responses—they’ve been really helpful in moving my project forward.
I believe I’ve resolved most of the issues I was facing. Regarding your last message, you suggested using the following code snippet:
if (
args.requestType === 'filterchoicerequest' ||
args.requestType === 'filterSearchBegin'
) {
// remove query for distinct value
args.query.distincts = [];
// select current column
args.query.select(args.filterModel.options.field);
}
I’m not entirely sure what this code is supposed to do in my specific case.
Also, I’m experiencing another issue: when I open the Excel-style filter window and type something in the search field, all the values disappear—regardless of what I type. Even if I clear the search field, the values don’t reappear. I have to close and reopen the filter window to reload the data from the backend.
Is the code you provided supposed to address this behavior? Because whether I include it or not, it doesn’t seem to make any difference.
Thanks again!
Hi DEVAUX,
The code you are referring to is specifically designed to include “select” query only the current column field in the request, allowing the server to return only the relevant data. The "distinct" property is set to an empty array because, by default, when both "select" and "distinct" are used together in a query, the "select" clause is ignored. Since your requirement is to fetch values for only the selected column when opening the filter and performing a search, this approach has been implemented.
With this configuration, the column name is included in the API request, enabling the server to execute a "SELECT DISTINCT" operation for that specific column and return the appropriate filtered values. Without including the "select" field in the request, the server cannot identify which column’s data is being requested for the checkbox filter. In such cases, the server would be forced to return records containing all column fields, which is not optimal.
Regarding your query about the filter search returning an empty list, we suspect that the issue is due to the server not returning data in the expected format. Please note that when the Grid is initially loaded or when the filter popup is opened, the API request will contain a "requiresCounts" property with the value set to "true". In this case, the server must return the response in the following structure: “{ "result": [...], "count": ... }”.
However, during a filter search operation, the API request does not include the "requiresCounts" property or may explicitly set it to "false". In such cases, the server should respond with a plain array of records instead of the usual "result" and "count" format. Please refer to the following sample code for clarification:
|
[Controller (server-side)]
public IActionResult UrlDatasource([FromBody] DataManagerRequest dm) { var gridData = order; IEnumerable DataSource = gridData;
// Handle all the Grid actions here
// Return the data based on RequiresCounts return dm.RequiresCounts ? Json(new { result = DataSource, count }) : Json(DataSource); }
|
It is essential to return data in the expected format because the Grid depends on the response structure for proper rendering and behavior. Any deviation from the expected format may result in issues such as the one you are currently experiencing.
Regards,
Santhosh I
Hello,
I’m reopening this ticket to ask a question. When I open an Excel-style filter popup, the filter values are correctly retrieved from the backend using the DataManager — everything works fine. However, when I perform a search, it triggers a backend request to refresh the list.
Is there a way to prevent this request from being made? Since all the filter values are already fetched when the popup opens, it would be more efficient to perform the search on the client side. As it stands, I don’t feel like I'm gaining performance compared to the previous approach where I wasn't using DataManager for filters.
Thank you!
Hi DEVAUX,
By default, the Excel filter in the Grid component retrieves only the first 1000 records when the filter popup is opened. This behavior is intended to ensure optimal performance when handling large datasets.
As a result, records beyond the initial 1000 are retrieved only when a search is performed, which is why a request is sent to the backend during filtering. There is no built-in option to override this behavior directly. However, based on your previous updates, since you are already using a separate DataManager for the Excel filter checkbox dataSource, you can modify this behavior by enabling offline mode in the DataManager by setting the "offline" property to "true".
Please refer to the code snippet and sample below for reference:
|
[data.service.ts]
public getExcelFilterWithCustomAdaptor() { return new DataManager({ url: this.BASE_URL, adaptor: new UrlAdaptor(), offline: true, }); }
|
Sample: https://stackblitz.com/edit/angular-pqeq2j-5xbvsmc7
By enabling offline mode, the initial request will fetch all records from the backend, and subsequent operations (searching) inside the excel filter, will be handled entirely on the client side using the fetched data.
You can find more information DataManager offline mode from the documentation below:
Work in offline mode in Angular Data component | Syncfusion
Regards,
Santhosh I
Thank you for your response!
Indeed, setting "offline": true in the DataManager prevents additional requests during filter searches, which is great. However, I’ve noticed that when the filter popup opens, two requests are now being sent instead of one.
Could you please clarify why this happens? Are both requests necessary for the component to function correctly? I attempted to prevent the duplicate call using a caching mechanism, but doing so resulted in an error that prevented the filters from loading.
Also, I have another question:
When clicking on a column header to sort, is it possible to cancel the previous request if a new sort action is triggered quickly after? Ideally, I would like to allow users to re-click immediately to change the sort direction without having to wait for the previous request to complete. Is there a recommended approach for achieving this with DataManager?
Thank you for your support!
Best regards,
Hi DEVAUX,
Based on your request, we have provided the “offline” mode to achieve the expected behavior. While this functions as intended and prevents additional requests from being sent during a search, as previously explained, the Excel filter itself does not provide a way to prevent this behavior. Consequently, the Excel filter will, by default, send a request that includes the query. When “offline” mode is enabled in DataManager, it is configured to send a request without a query. Since both of these cases occur simultaneously, two requests are sent concurrently, and it is not possible to prevent this with the current approach.
However, you can achieve the desired outcome using an alternative method. Instead of using the “offline” mode, you can request the entire data set for the filter during the initial render and bind it to the filter checkbox dataSource whenever the filter is opened. This approach ensures that the filter data request is sent only once at the initial render and reused each time the filter is opened. Please find the code snippet and modified sample below:
|
[app.component.ts]
created() { this.dataService .getExcelFilterWithCustomAdaptor() .executeQuery(new Query()) .then((res: any) => { this.filterData = res.result; }); }
actionBegin(args) { if (args.requestType === 'filterBeforeOpen') { if (this.filterData) { // new remote data source for checkbox list args.filterModel.options.dataSource = this.filterData;
// remove filtered field except current column field args.filterModel.options.filteredColumns = args.filterModel.options.filteredColumns.filter( (col) => col.field === args.columnName ); } } }
|
Sample: https://stackblitz.com/edit/angular-pqeq2j-pxqmcaky
Additionally, we have branched your second query into a new forum thread to maintain clarity in our responses. Please follow up on the forum below for further updates regarding your query about sorting:
Forum: Canceling Previous DataManager... | Angular - EJ 2 Forums | Syncfusion®
Kindly create new tickets for any new queries to facilitate easier follow-up for you and other community users seeking solutions to similar queries.
Regards,
Santhosh I