Custom Adaptor

I am facing a problem and can't solve it. I have an EndPoint to retrieve data for my PivotTable, I can't use the standard URL to retrieve the data because I need to call it through my API Client.


For this, following the recommendation from the documentation, I created my own Adaptor for the DataManager and tried to make it return the data. However, when using my Adaptor, it starts to be called endlessly, meaning processQuery is called several times in a second and never stops. If I use a normal URL and a standard adapter, I get a response immediately and only 1 time. So I'm wondering what the problem is, are there any practical examples of using my adapter? Maybe there is some other way to use my EndPoint with ApiClient? Below I provide some parts of the code that may help:


export class CustomPivotAdaptor extends WebApiAdaptor {
  private apiClient: any;
  private domainId: number;
  private processId: number;
  private isLoading = false;

  constructor(apiClient: any, domainId: number, processId: number) {
    super();
    this.apiClient = apiClient;
    this.domainId = domainId;
    this.processId = processId;
  }

  public processQuery(dm: DataManager, query: Query): Promise<Object> {
    if (this.isLoading) {
      return Promise.resolve({ result: [], count: 0 });
    }

    this.isLoading = true;

    console.log("processQuery called", { query: query.queries });

    return this.apiClient.dispatch(this.domainId, this.processId)
      .then((response: any) => {
        console.log("API response received", response);
        return this.processResponse(response);
      })
      .catch((error: any) => {
        console.error("API call failed", error);
        return { result: [], count: 0 };
      })
      .finally(() => {
        this.isLoading = false;
      });
  }

  public processResponse(data: any): any {
    if (Array.isArray(data)) {
      return {
        result: data,
        count: data.length
      };
    }

    if (data && data.result) {
      return data;
    }

    if (data && typeof data === "object") {
      const possibleArrays = Object.values(data).filter((element) => Array.isArray(element));
      if (possibleArrays.length > 0) {
        const resultArray = possibleArrays[0] as any[];
        return {
          result: resultArray,
          count: resultArray.length
        };
      }
    }

    console.warn("Unexpected data format", data);
    return {
      result: [],
      count: 0
    };
  }
}

const adaptor = new CustomPivotAdaptor(GetPivotData, 1074, 5089);


  const remoteData = React.useMemo(() => {
    return new DataManager({
      url: "https://jsonplaceholder.typicode.com/posts",
      adaptor,
      offline: false,
      requiresFormat: false
    });
  }, []);
  const dataSourceSettings = React.useMemo((): IDataOptions => {
    return {
      enableSorting: true,
      columns: [{ name: "Date_Y" }, { name: "Date_Q" }, { name: "Date_M" }],
      rows: [{ name: "State" }, { name: "TotalCosts", caption: "Total Costs" }, { name: "TotalRevenue", caption: "Total Revenue" }],
      formatSettings: [{ name: "Profit", format: "C0" }],
      drilledMembers: [{ name: "State", items: ["Approved", "Pending"] }],
      filterSettings: [{
        name: "State", type: "Exclude", items: ["Rejected"]
      }],
      dataSource: remoteData,
      expandAll: false,
      values: [
        { name: "Profit", caption: "Net Profit", showValueTypeIcon: false },
        { name: "TotalRevenue", caption: "Total Revenue", showValueTypeIcon: false },
        { name: "TotalCosts", caption: "Total Costs", showValueTypeIcon: false }
      ],
      filters: [],
      fieldMapping: [
        { name: "rank_display", dataType: "number", showNoDataItems: true },
        { name: "country", caption: "Country", showNoDataItems: true },
        { name: "city", caption: "City", showNoDataItems: true },
        { name: "region", caption: "Region", showNoDataItems: true },
        { name: "research_output", caption: "Research Output", showNoDataItems: true },
        { name: "student_faculty_ratio", caption: "Student faculty ratio", showNoDataItems: true }
      ],
    };
  }, [remoteData]);

3 Replies

NK Nikita Kvasnin May 27, 2025 08:28 AM UTC

Another try:


  const remoteData = React.useMemo(() => {
    return new DataManager({
      adaptor: new CustomDataAdaptor({
        getData (option: FetchOption) {
          let request: object;
          GetPivotData.dispatch(1074, 5089)
            .then((data) => {
              console.log({
                result: data,
                count: data.length
              });
              option?.onSuccess?.({
                result: data,
                count: data.length
              });
            }).catch((error) => {
              option?.onFailure?.(error);
            });
        },
      }),
    });
  }, []);

Image_3731_1748334419187


If instead of object I'll pass there just list of data it called just once


              option?.onSuccess?.(data);



SK Sridhar Karunakaran Syncfusion Team May 27, 2025 01:43 PM UTC

Hi Nikita,


Thank you for sharing the code example. We understand that you are currently facing an issue where the processQuery method is triggered multiple times when using a custom adaptor that extends WebApiAdaptor. If this is the case, we were able to reproduce the issue on our end and are actively investigating to identify the root cause. We will provide further updates within two business days, by May 29, 2025.


We appreciate your patience until then.


Regards,

Sridhar Karunakaran.



SK Sridhar Karunakaran Syncfusion Team May 29, 2025 01:55 PM UTC

Hi Nikita,


Thank you for your continued patience.


After reviewing your latest code example, we observed that the data passed to the onSuccess method is returned as an object containing both result and count properties. We’d like to clarify that the Pivot Table component expects the data source to be either an list of objects or CSV data—not wrapped within another object.


When the data is returned in a wrapped format, the engine does not initiate its processing logic, which leads to the issue you're encountering. To resolve this, please ensure that the data is returned directly as a flat list of objects, as demonstrated in your alternate example.


This approach ensures that the Pivot Table engine receives the data in the expected format and processes it correctly.


Code example:

const remoteData = React.useMemo(() => {

  return new DataManager({

    adaptor: new CustomDataAdaptor({

      getData (option: FetchOption) {

        let request: object;

        GetPivotData(1074, 5089)

          .then((data) => {

            console.log({

              result: data,

              count: data.length

            });

            option?.onSuccess?.(data);

          }).catch((error) => {

            option?.onFailure?.(error);

          });

      },

    }),

  });

}, []);

 

Output screenshot:


In the meantime, we’ve prepared a sample based on the provided code for your reference. Please find it attached below.


Regards,

Sridhar Karunakaran


Attachment: Sample_c0735fcc.zip

Loader.
Up arrow icon