TypeError Cannot read properties of undefined (reading 'refreshUI')

Hello, I am getting an error when I try to refresh columns in my grid.

It gives me this error: 

TypeError: Cannot read properties of undefined (reading 'refreshUI')

on this piece of code: 

Grid.prototype.refresh = function () {

        if (!this.isDestroyed) {

            this.isManualRefresh = true;

            this.headerModule.refreshUI();

            this.updateStackedFilter();

            this.renderModule.refresh();

        }

    };

The code where I am tryin to refresh the grid:


private loadGridState() {
    setTimeout(() => {
      const selectedTab = this.tabs.selectedItem;
      const gridState = localStorage.getItem(`gridState${selectedTab}`);
      if (gridState && this.grid) {
        const persistData = JSON.parse(gridState);
        this.resetGridToDefault();
        this.grid.columns = this.rebuildColumns(this.grid.columns as Column[], persistData.columns);
        this.selectedFrozenColumns = persistData.selectedFrozenColumns;
        this.filterSettings = persistData.filterSettings;
        this.sortSettings = persistData.sortSettings;
        this.lines = persistData.lines;
        this.applyFreezing();
        this.grid.refresh();
      } else {
        this.resetGridToDefault();
      }
    }, 0);
  }

  private rebuildColumns(gridColumns: Column[], persistedColumns: Column[]): Column[] {
    return gridColumns.map((column: Column, i: number) => {
      const persistedColumn = persistedColumns[i];
      return Object.assign(column, {
        width: persistedColumn.width,
        freeze: persistedColumn.freeze,
        visible: persistedColumn.visible,
        customAttributes: persistedColumn.customAttributes,
        index: persistedColumn.index,
      });
    });
  }

  private resetGridToDefault() {
    this.grid.clearSorting();
    this.grid.clearFiltering();
    this.grid.clearSelection();
    this.selectedFrozenColumns = [];
    this.lines = 'Default';
    this.applyFreezing();
    this.grid.refresh();
  }




3 Replies

DM Dineshnarasimman Muthu Syncfusion Team September 2, 2024 10:22 AM UTC

Hi Niels Van Goethem,


By reviewing your query, we understand that you are setting the grid properties such as filterSettings, sortSettings and grid columns from the local storage of the browser and then you are refreshing the grid. We suggest you to use setProperties method of the grid to set the properties that you get from the local storage. We have modified the sample and attached the code snippet for your reference.


  loadGridState() {

    setTimeout(() => {

      const gridState = localStorage.getItem(`gridState`);

      if (gridState && this.grid) {

        const persistData = JSON.parse(gridState);

        this.grid.columns = this.rebuildColumns(

          this.grid.columns as Column[],

          persistData.columns

        );

        this.grid.setProperties({

          sortSettings: persistData.sortSettings,

          filterSettings: persistData.filterSettings,

          columns: this.grid.columns,

        });

      } else {

        this.resetGridToDefault();

      }

    }, 0);

  }

 


Sample: https://stackblitz.com/edit/angular-ura3p4?file=src%2Fapp.component.ts


Please let us know if you need any further assistance.


Regards,

Dineshnarasimman M



NV Niels Van Goethem September 2, 2024 10:39 AM UTC

Hi, I did as you said, but am still encountering the same error


import {
  AfterViewInit,
  Component,
  OnInit,
  QueryList,
  ViewChild,
  ViewChildren,
} from '@angular/core';
import { UntypedFormGroup, FormBuilder } from '@angular/forms';
import {
  Column,
  ColumnChooserService,
  FilterService,
  FilterSettings,
  FreezeService,
  GridComponent,
  GridLine,
  LoadingIndicator,
  PageService,
  ReorderService,
  ResizeService,
  SelectionService,
  SelectionSettings,
  SortService,
  SortSettings,
  ToolbarService,
} from '@syncfusion/ej2-angular-grids';
import { TabComponent } from '@syncfusion/ej2-angular-navigations';
import { TranslateService } from '@ngx-translate/core';
import { Status } from '@shared/enums/status.enum';
import { FilterPanelComponent } from '@shared/components';
import { Department } from '../../models';

import data from '../../../../../../mocks/my-projects.mock.json';
import departmentData from '../../../../../../mocks/department-tree.mock.json';

@Component({
  selector: 'am-my-projects',
  templateUrl: './my-projects.component.html',
  styleUrls: ['./my-projects.component.scss'],
  providers: [
    ToolbarService,
    ColumnChooserService,
    SelectionService,
    PageService,
    FilterService,
    FreezeService,
    ReorderService,
    ResizeService,
    SortService,
  ],
})
export class MyProjectsComponent implements OnInit, AfterViewInit {
  @ViewChildren('grid') public grids: QueryList<GridComponent>;
  @ViewChild('tabs') public tabs: TabComponent;
  @ViewChild('filterPanel') public filterPanel: FilterPanelComponent;

  public grid: GridComponent;
  public status = Status;

  public projectLeadData: any[] = [];
  public userLeadData: any[] = [];
  public sponsorData: any[] = [];
  public resourceData: any[] = [];
  public projectLeadDataCount = 0;
  public userLeadDataCount = 0;
  public sponsorDataCount = 0;
  public resourceDataCount = 0;

  // Toolbar states for each tab
  public projectLeadToolbarState: any | undefined;
  public userLeadToolbarState: any | undefined;
  public sponsorToolbarState: any | undefined;
  public resourceToolbarState: any | undefined;

  public loadingIndicator: Partial<LoadingIndicator>;
  public selectOptions: Partial<SelectionSettings>;
  public toolbar: (string | object)[];
  public filterSettings: Partial<FilterSettings>;
  public sortSettings: Partial<SortSettings>;
  public lines: GridLine;
  public freezingOptions: { text: string; field: string }[];
  public selectedFrozenColumns: string[] = [];

  public headers: { text: string }[] = [];

  public externalFilters: UntypedFormGroup | undefined;
  public statusOptions: { value: Status; label: string }[] = [];
  public statusFields: { text: string; value: string };

  public departments: Department[] = [];
  public departmentFields: any | undefined;

  constructor(
    private readonly translateService: TranslateService,
    private readonly fb: FormBuilder
  ) {}

  public ngOnInit(): void {
    this.departments = departmentData;
    this.loadingIndicator = { indicatorType: 'Shimmer' };
    this.projectLeadData = data.filter((x) => x.role === 'Project Lead');
    this.projectLeadDataCount = this.projectLeadData.length;
    this.userLeadData = data.filter((x) => x.role === 'User Lead');
    this.userLeadDataCount = this.userLeadData.length;
    this.sponsorData = data.filter((x) => x.role === 'Sponsor');
    this.sponsorDataCount = this.sponsorData.length;
    this.resourceData = data.filter((x) => x.role === 'Resource');
    this.resourceDataCount = this.resourceData.length;
    this.selectOptions = { persistSelection: true };
    this.configureToolbar();
    this.configureHeaders();
    this.configureExternalFilters();
    this.statusFields = { text: 'label', value: 'value' };
    this.configureStatusOptions();
    this.departmentFields = {
      dataSource: this.departments,
      value: 'id',
      text: 'name',
      child: 'children',
    };
  }

  public ngAfterViewInit() {
    this.setActiveGrid();
    this.loadGridState();
  }

  public openColumnChooser() {
    const gridElem = this.grid.getContent();
    const gridRect = gridElem.getBoundingClientRect();

    this.grid.columnChooserModule.openColumnChooser(gridRect.right - gridRect.left - 250, 38);
  }

  public filterChangeHandler(type: string) {
    this.filterSettings = { type: type as FilterSettings['type'] };
    setTimeout(() => {
      this.saveGridState();
    }, 0);
  }

  public gridLinesChangeHandler(type: string) {
    this.lines = type as GridLine;
    this.saveGridState();
    setTimeout(() => {
      this.grid.refresh();
    }, 0);
  }

  public handleFreezeChange(args: any, fieldColumn: string) {
    const isChecked = args.checked;
    if (isChecked) {
      this.selectedFrozenColumns.push(fieldColumn);
    } else {
      this.selectedFrozenColumns = this.selectedFrozenColumns.filter((x) => x !== fieldColumn);
    }
    this.grid.refreshColumns();
    this.applyFreezing();
    this.saveGridState();
  }

  public dataBound() {
    this.configureFreezingOptions();
  }

  public configureHeaders() {
    this.headers.push({
      text: `${this.translateService.instant('Dashboard.MyProjects.Tabs.ProjectLead')} (${
        this.projectLeadData.length
      })`,
    });
    this.headers.push({
      text: `${this.translateService.instant('Dashboard.MyProjects.Tabs.UserLead')} (${
        this.userLeadData.length
      })`,
    });
    this.headers.push({
      text: `${this.translateService.instant('Dashboard.MyProjects.Tabs.Sponsor')} (${
        this.sponsorData.length
      })`,
    });
    this.headers.push({
      text: `${this.translateService.instant('Dashboard.MyProjects.Tabs.Resource')} (${
        this.resourceData.length
      })`,
    });
  }

  public getStatusClass(status: Status): string {
    switch (status) {
      case Status.Idea:
        return 'idea';
      case Status.IdeaAccepted:
        return 'idea-accepted';
      case Status.IdeaToBeApprovedByMAC:
        return 'idea-to-be-approved';
      case Status.IdeaRejected:
        return 'idea-rejected';
      case Status.InPreperation:
        return 'in-preperation';
      case Status.InExecution:
        return 'in-execution';
      case Status.OnHold:
        return 'on-hold';
      case Status.Stopped:
        return 'stopped';
      case Status.Aftercare:
        return 'aftercare';
      case Status.Finished:
        return 'finished';
      default:
        return '';
    }
  }

  // Switch because we work with local data
  // When this is remote data, this will be done on server
  public searchList(searchTerm: string) {
    const selectedTab = this.tabs.selectedItem;
    switch (selectedTab) {
      case 0:
        this.projectLeadData = data.filter(
          (x) =>
            x.role === 'Project Lead' &&
            (!searchTerm ||
              Object.keys(x).some((key) =>
                x[key]?.toString().toLowerCase().includes(searchTerm.toLowerCase())
              ))
        );
        break;
      case 1:
        this.userLeadData = data.filter(
          (x) =>
            x.role === 'User Lead' &&
            (!searchTerm ||
              Object.keys(x).some((key) =>
                x[key]?.toString().toLowerCase().includes(searchTerm.toLowerCase())
              ))
        );
        break;
      case 2:
        this.sponsorData = data.filter(
          (x) =>
            x.role === 'Sponsor' &&
            (!searchTerm ||
              Object.keys(x).some((key) =>
                x[key]?.toString().toLowerCase().includes(searchTerm.toLowerCase())
              ))
        );
        break;
      case 3:
        this.resourceData = data.filter(
          (x) =>
            x.role === 'Resource' &&
            (!searchTerm ||
              Object.keys(x).some((key) =>
                x[key]?.toString().toLowerCase().includes(searchTerm.toLowerCase())
              ))
        );
        break;
      default:
        break;
    }
  }

  public toggleFilterPanel() {
    this.filterPanel.togglePanel();
  }

  // Switch because we work with local data
  // When this is remote data, this will be done on server
  public applyExternalFilters() {
    const selectedTab = this.tabs.selectedItem;
    const externalFilters = this.externalFilters.value;
    const { status, startDate, endDate, department } = externalFilters;

    // Define a function to apply all filters
    const applyFilters = (y) => {
      return y
        .filter((x) => !status || status.length === 0 || status.some((z) => z === x.status)) // Filter by status if provided
        .filter((x) => !startDate || new Date(x.startDate) >= new Date(startDate)) // Filter by startDate if provided
        .filter((x) => !endDate || new Date(x.endDate) <= new Date(endDate)) // Filter by endDate if provided
        .filter(
          (x) =>
            !department ||
            department.length === 0 ||
            department.some((z) => parseInt(z, 10) === x.departmentId)
        ); // Filter by department if provided
    };
    switch (selectedTab) {
      case 0:
        this.projectLeadData = applyFilters(data.filter((x) => x.role === 'Project Lead'));
        break;
      case 1:
        this.userLeadData = applyFilters(data.filter((x) => x.role === 'User Lead'));
        break;
      case 2:
        this.sponsorData = applyFilters(data.filter((x) => x.role === 'Sponsor'));
        break;
      case 3:
        this.resourceData = applyFilters(data.filter((x) => x.role === 'Resource'));
        break;
      default:
        break;
    }
  }

  public saveGridState() {
    const persistData = this.grid.getPersistData();
    const persistDataObj = JSON.parse(persistData);
    const selectedTab = this.tabs.selectedItem;
    persistDataObj['lines'] = this.lines;
    persistDataObj['selectedFrozenColumns'] = this.selectedFrozenColumns;
    localStorage.setItem(`gridState${selectedTab}`, JSON.stringify(persistDataObj));
  }

  public onTabChange() {
    this.setActiveGrid();
    this.loadGridState();
  }

  private applyFreezing() {
    (this.grid.columns as Column[]).forEach((column: Column) => {
      if (
        (this.selectedFrozenColumns.includes(column.field) && column.freeze !== 'Right') ||
        (column.customAttributes && column.customAttributes['class'] === 'status')
      ) {
        column.freeze = 'Left';
      } else if (column.type !== 'checkbox' && column.index !== 0 && column.freeze !== 'Right') {
        column.freeze = 'None';
      }
    });
    this.grid.refreshColumns();
  }

  private configureToolbar() {
    const filterTypeText =
      this.translateService.instant('Dashboard.MyProjects.FilterType.Text') ?? 'Filter type';
    const filterTypeTooltip =
      this.translateService.instant('Dashboard.MyProjects.FilterType.Tooltip') ??
      'Choose filter type';

    const gridLinesText =
      this.translateService.instant('Dashboard.MyProjects.GridLines.Text') ?? 'Grid lines';
    const gridLinesTooltip =
      this.translateService.instant('Dashboard.MyProjects.GridLines.Tooltip') ??
      'Choose grid lines';

    const freezingText =
      this.translateService.instant('Dashboard.MyProjects.Freezing.Text') ?? 'Freezing';
    const freezingTooltip =
      this.translateService.instant('Dashboard.MyProjects.Freezing.Tooltip') ??
      'Choose fixed columns/rows';
    this.toolbar = [
      { text: filterTypeText, tooltipText: filterTypeTooltip, id: 'filterType' },
      { text: gridLinesText, tooltipText: gridLinesTooltip, id: 'gridlines' },
      { text: freezingText, tooltipText: freezingTooltip, id: 'freezing' },
      'ColumnChooser',
    ];
  }

  private configureFreezingOptions() {
    if (this.grid) {
      this.freezingOptions = this.grid
        ?.getVisibleColumns()
        .map((column) => ({ text: column.headerText, field: column.field }))
        .filter((x) => !!x.field)
        .sort((a, b) => a.text?.localeCompare(b.text));
    }
  }

  private configureExternalFilters() {
    this.externalFilters = this.fb.group({
      status: this.fb.control([]),
      startDate: this.fb.control(undefined),
      endDate: this.fb.control(undefined),
      department: this.fb.control([]),
    });
  }

  private configureStatusOptions() {
    this.statusOptions = [
      { value: Status.Idea, label: this.translateService.instant('Status.Idea') },
      { value: Status.IdeaAccepted, label: this.translateService.instant('Status.IdeaAccepted') },
      {
        value: Status.IdeaToBeApprovedByMAC,
        label: this.translateService.instant('Status.IdeaToBeApprovedByMAC'),
      },
      { value: Status.IdeaRejected, label: this.translateService.instant('Status.IdeaRejected') },
      { value: Status.InPreperation, label: this.translateService.instant('Status.InPreperation') },
      { value: Status.InExecution, label: this.translateService.instant('Status.InExecution') },
      { value: Status.OnHold, label: this.translateService.instant('Status.OnHold') },
      { value: Status.Stopped, label: this.translateService.instant('Status.Stopped') },
      { value: Status.Aftercare, label: this.translateService.instant('Status.Aftercare') },
      { value: Status.Finished, label: this.translateService.instant('Status.Finished') },
    ];
  }

  private loadGridState() {
    setTimeout(() => {
      const selectedTab = this.tabs.selectedItem;
      const gridState = localStorage.getItem(`gridState${selectedTab}`);
      if (!this.grid) {
        return;
      }
      if (gridState) {
        const persistData = JSON.parse(gridState);
        this.resetGridToDefault();
        this.grid.columns = this.rebuildColumns(this.grid.columns as Column[], persistData.columns);
        this.selectedFrozenColumns = persistData.selectedFrozenColumns || [];
        this.grid.setProperties({
          sortSettings: persistData.sortSettings,
          filterSettings: persistData.filterSettings,
          columns: this.grid.columns,
        });
        this.lines = persistData.lines;
        this.grid.refreshColumns();
        this.applyFreezing();
      } else {
        this.resetGridToDefault();
      }
    }, 0);
  }

  private rebuildColumns(gridColumns: Column[], persistedColumns: Column[]): Column[] {
    return gridColumns.map((column: Column, i: number) => {
      const persistedColumn = persistedColumns[i];
      return Object.assign(column, {
        width: persistedColumn.width,
        freeze: persistedColumn.freeze,
        visible: persistedColumn.visible,
        customAttributes: persistedColumn.customAttributes,
        index: persistedColumn.index,
      });
    });
  }

  private resetGridToDefault() {
    this.grid.clearSorting();
    this.grid.clearFiltering();
    this.grid.clearSelection();
    this.selectedFrozenColumns = [];
    this.lines = 'Default';
    this.filterSettings = { type: 'FilterBar' };
    this.grid.refreshColumns();
    this.applyFreezing();
  }

  private setActiveGrid() {
    setTimeout(() => {
      const selectedTab = this.tabs.selectedItem;
      const gridArray = this.grids.toArray();
      this.grid = gridArray[selectedTab];
    }, 0);
  }
}



AR Aishwarya Rameshbabu Syncfusion Team September 5, 2024 12:32 PM UTC

Hi Niels Van Goethem,


Based on the provided code example, we have updated the sample but were unable to reproduce the issue. Please refer to the following sample:

Sample: https://stackblitz.com/edit/angular-ura3p4-ntlgtz?file=src%2Fapp.component.ts

Also, we suspect that the script issue you are encountering arises from refreshing the Grid prior to its complete creation, so please ensure that you are refreshing the Grid after it has been created.


If the issue persists, kindly provide the following details that would be very helpful for us to validate it further:

1. From the shared code, it appears you are rendering the Grid within a tab component and facing challenges with maintaining the Grid state. Please detail your specific requirements regarding which Grid properties you wish to persist, when to load the persisted data, and when to display the default Grid.

2. Provide the complete Grid rendering code along with the code lines of its parent container (Tab Component).

3. Specify the type of data binding used in the Grid—whether it is remote or local data. If remote data is used, please provide adaptor details.

4. Indicate the version of the Syncfusion package you are currently using.

5. Share a sample that replicates the issue or attempt to replicate the issue using the provided sample.

6. Provide a video demonstration showing the issue replication scenario.



Regards

Aishwarya R


Loader.
Up arrow icon