Ag Grid Why cell renderer component updates after running 'onRowHeightChanged()'

Viewed 173

I use Ag Grid Angular to display the user list. One of the cells is an email with a custom component. When the value is invalid it shows an error message. To display it properly I should expend the row. When I expend the row, an error despaired. It looks like the cell renderer component updates every time after calling the onRowHeightChanged() method. How to fix this issue?

  <div class="app-users-list__action-bar">
    <button
      class="btn btn-primary ml-1"
      (click)="changeGridMode()"
    >{{gridModeBtnTitle}}</button>
  </div>

  <ng-container *ngIf="users$ | async"></ng-container>
  <div
    class="app-users-list__table"
    infiniteScroll
    [infiniteScrollContainer]="'.ag-body-viewport'"
    [infiniteScrollDistance]="2"
    [infiniteScrollThrottle]="0"
    (scrolled)="onScroll()"
    [scrollWindow]="false">

    <ag-grid-angular
      class="ag-theme-bootstrap user-list"
      [suppressRowClickSelection]="true"
      [gridOptions]="gridOptions"
      [frameworkComponents]="frameworkComponents"
    >
    </ag-grid-angular>
  </div>

  <ngx-loading [show]="isLoading$ | async"></ngx-loading>
</section>


import { Component, OnInit } from '@angular/core';
import { NgModel } from '@angular/forms';

import { select, Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import * as fromShared from '../../../shared/models';
import * as userList from '../../actions/user-list.action';
import * as fromUserListSelectors from '../../selectors/user-list.selectors';
import * as gridComponents from '../../../shared/grid-components';

import { Organization } from '../../../organizations';
import { DEFAULT_INTERACTION_LIST_FILTER, SORT_DIRECTION } from '../../../shared/constants/filter.constant';
import { GridApi, GridOptions, GridReadyEvent, RowNode } from 'ag-grid-community';
import { Evaluator } from '../../../evaluators';

@Component({
  selector: 'app-user-list',
  templateUrl: './user-list.component.html',
  styleUrls: ['./user-list.component.scss'],
})
export class UserListComponent implements OnInit {

  public users$: Observable<fromShared.User[]>;
  public isLoading$: Observable<boolean>;
  public isEditMode = false;
  public frameworkComponents = {...gridComponents};
  public gridModeBtnTitle = this.setButtonTitle(this.isEditMode);
  public columnDefs = [
    {
      headerName: 'First Name',
      field: 'firstName',
      minWidth: 200,
      unSortIcon: true,
      suppressMovable: true,
      resizable: true,
      autoHeight: true,
      cellRenderer: 'GridTextCellComponent',
      cellRendererParams: (params) => {
        return {
          value: params.data.firstName,
          name: 'firstName',
          aqaId: 'user-firstName',
        };
      }
    },
    {
      headerName: 'Last Name',
      field: 'lastName',
      minWidth: 200,
      unSortIcon: true,
      suppressMovable: true,
      autoHeight: true,
      cellRenderer: 'GridTextCellComponent',
      cellRendererParams: (params) => {
        return {
          value: params.data.lastName,
          name: 'lastName',
          aqaId: 'user-lastName',
        };
      }
    },
    {
      headerName: 'Email',
      field: 'email',
      minWidth: 200,
      unSortIcon: true,
      suppressMovable: true,
      autoHeight: true,
      cellRenderer: 'GridEmailCellComponent',
      cellRendererParams: (params) => {
        return {
          value: params.data.email,
          name: 'email',
          aqaId: 'user-email',
        };
      }
    },
    {
      headerName: 'Sso Email Id',
      field: 'SsoEmail',
      minWidth: 300,
      unSortIcon: true,
      suppressMovable: true,
      autoHeight: true,
      cellRenderer: 'GridEmailCellComponent',
      cellRendererParams: (params) => {
        return {
          editable: true,
          editMode: this.isEditMode,
          value: params.data.ssoEmail,
          name: 'ssoEmail',
          placeholder: 'Enter a SSO email',
          aqaId: 'user-ssoEmail',
          callback: this.updateHandler.bind(this, params.data, 'ssoEmail'),
          statusChange: this.statusFieldChange.bind(this, params.node)
        };
      }
    },
    {
      headerName: 'Org.Admin',
      field: 'orgAdmin',
      maxWidth: 200,
      unSortIcon: false,
      suppressMovable: true,
      autoHeight: true,
      cellRenderer: 'GridTextCellComponent',
      cellRendererParams: (params) => {
        return {
          value: this.setUserRoleStatus(params.data.roles, fromShared.ERole.ORG_ADMIN),
          name: 'orgAdmin',
          aqaId: 'user-orgAdmin',
        };
      }
    },
    {
      headerName: 'Eval.Admin',
      field: 'evalAdmin',
      maxWidth: 200,
      unSortIcon: false,
      suppressMovable: true,
      autoHeight: true,
      cellRenderer: 'GridTextCellComponent',
      cellRendererParams: (params) => {
        return {
          value: this.setUserRoleStatus(params.data.roles, fromShared.ERole.EVALUATION_ADMIN),
          name: 'evalAdmin',
          aqaId: 'user-evalAdmin',
        };
      }
    },
    {
      headerName: 'Evaluator',
      field: 'evaluator',
      maxWidth: 200,
      unSortIcon: false,
      suppressMovable: true,
      autoHeight: true,
      cellRenderer: 'GridTextCellComponent',
      cellRendererParams: (params) => {
        return {
          value: this.setUserRoleStatus(params.data.roles, fromShared.ERole.CAN_VOTE),
          name: 'evaluator',
          aqaId: 'user-evaluator',
        };
      }
    }
  ];
  public gridOptions = <GridOptions>{
    context: {componentParent: this},
    columnDefs: this.columnDefs,
    rowData: [],
    enableColResize: true,
    enableSorting: true,
    headerHeight: 40,
    rowHeight: 40,
    animateRows: true,
    suppressLoadingOverlay: false,
    onSortChanged: () => {
      let sortModel = this.gridOptions.api.getSortModel();
      if (sortModel.length > 0) {
        this.usersFilter.direction = SORT_DIRECTION[sortModel[0].sort.toUpperCase()];
        this.usersFilter.orderBy = sortModel[0].colId;
      } else {
        this.usersFilter.orderBy = null;
        this.usersFilter.direction = DEFAULT_INTERACTION_LIST_FILTER.direction;
      }
      this.usersFilter.page = 0;
      this.gridOptions.api.setRowData([]);
      this.store.dispatch(new userList.LoadUsersByFilterAction(this.usersFilter));
    },
    onRowDataChanged: (params) => {
    },
    getRowNodeId: (data: Evaluator) => {
      return data.id;
    },
    noRowsOverlayComponent: 'NoRowsOverlayComponent',
    noRowsOverlayComponentParams: {
      noRowsMessageFunc: () => {
        this.isLoading$.pipe(
          map(loading => loading ? '' : 'You have no Users.')
        );
      }
    },
    rowClassRules: {
      'text-transparent': (params) => params.data.unused,
    },
    defaultColDef: {
      headerName: '',
      field: '',
      minWidth: 100,
      unSortIcon: true,
      suppressMovable: true,
      resizable: true,
      autoHeight: true,
    },
    onGridReady(event: GridReadyEvent) {
      event.api.sizeColumnsToFit();
    },
  };

  private usersFilter: fromShared.UserFilterList = {
    limit: 20,
    page: 0,
    orderBy: 'firstName',
    direction: SORT_DIRECTION.ASC
  };

  constructor(private store: Store<Organization>) {}

  public ngOnInit(): void {
    this.store.dispatch(new userList.LoadUsersByFilterAction(this.usersFilter));

    this.isLoading$ = this.store.pipe(select(fromUserListSelectors.selectLoading));
    this.users$ = this.store.pipe(select(fromUserListSelectors.selectUsers))
      .pipe(
        map(users => {
          const api: GridApi = this.gridOptions.api;
          if (api) {
            api.setRowData(users);
          }
          return users;
        })
      );
  }

  public onScroll() {
    this.usersFilter.page++;
    this.store.dispatch(new userList.LoadUsersByFilterAction(this.usersFilter));
  }

  public changeGridMode(): void {
    this.isEditMode = !this.isEditMode;
    this.gridModeBtnTitle = this.setButtonTitle(this.isEditMode);
    this.gridOptions.enableSorting = !this.isEditMode;
    this.gridOptions.api.refreshCells({force: true});
  }

  private updateHandler(item: fromShared.User, field: string, newValue: any, controller: NgModel) {
    if (controller && controller.invalid) {
      return;
    }
    item[field] = newValue;
    this.store.dispatch(new userList.UpdateUserAction(item));
  }

  private statusFieldChange(node: RowNode, status: string, isTouched: boolean) {
    if (!isTouched) {
      return;
    }
    if (status === 'INVALID') {
      if (node.rowHeight < 60) {
        this.updateRowSize(node, 60);
      }
    } else {
      setTimeout(() => this.updateRowSize(node, null));
    }
  }

  private updateRowSize(node: RowNode, height: number) {
    node.setRowHeight(height);
    this.gridOptions.api.onRowHeightChanged();
  }

  private setButtonTitle(isEditMode: boolean): string {
    return isEditMode ? 'Done' : 'Edit';
  }

  private setUserRoleStatus(roles: fromShared.ERole[], type: fromShared.ERole): boolean {
    return roles.includes(type);
  }

}
0 Answers
Related