Header checkbox selection doesn't work when using Server Side Data source
Asked Answered
E

3

13

As the title suggests, I'm using ag-grid with Angular, and I use a custom class that implements IServerSideDatasource for fetching data from API with rowModelType set to 'serverSide'.

The problem is that when I set headerCheckboxSelection and checkboxSelection to true, checkbox select appears next to each row but it doesn't appear in the Header, whereas it works perfectly fine when I use Client Side Row Model type.

Can anyone help?

Emancipated answered 11/9, 2019 at 14:5 Comment(0)
S
21

Header checkbox selection is not a supported feature of ag-grid's Server Side row model. You should be getting a console log message notifying you of this.

Please see the documentation for more information.

Feature                     Client-side   Infinite    Server-side     Viewport
...
Row Selection               ✔             ✔            ✔              ✔
Header Checkbox Selection   ✔             ✕            ✕              ✕

If you want to implement this functionality, you will need to manually handle this functionality using a custom header component. I've done it before (albeit, using Infinite Row-Model, not Server-side), but you need to keep track of a few things...

  • Total list of rows currently checked.
  • Has the 'check all' been selected?
  • Whether to show indeterminate checkbox (some, but not all rows selected).
  • When getting rows from the server, you need to see if check all button has been pressed and update that row's selection accordingly.

I used an Angular Service to keep a central location for tracking all this information, and just relied on the header-component for displaying the checkbox.

As you see, it is a non-trivial task, and there is no easy solution.

Scandium answered 11/9, 2019 at 14:30 Comment(1)
Can you please provide an example on how did you create this manually? Thanks!Hibernicism
J
2

As Rich said, Header checkbox selection is supported only in ag-grid's Client-side row model. But you can create your own headerComponent like this:

select-all.component.ts:

import { Component, OnDestroy } from '@angular/core';
import { IHeaderAngularComp } from 'ag-grid-angular';
import { IHeaderParams } from 'ag-grid-community';


@Component({
  selector: 'select-all',
  templateUrl: './select-all.component.html',
  styleUrls: ['./select-all.component.scss']
})
export class SelectAllComponent implements IHeaderAngularComp {
  headerName: string;
  isChecked: boolean;
  isIndeterminate: boolean;
  showCheckBox: boolean = true;
  sortIcon: string = 'none';
  sortSubIcon: string = 'none';
  params: any;


  agInit(params: IHeaderParams | any): void {
    this.params = params;
    this.headerName = this.params?.displayName;

    this.params.api.addEventListener('selectionChanged', () => {
      this.setCheckboxState();
    });

    this.showCheckBox = this.params?.checkboxSelection;

    this.setDefaultSortIcon();
  }

  toggleSelect(): void {
    this.isChecked = !this.isChecked;
    if (this.isChecked) {
      this.params?.api?.forEachNode((node) => {
        node.selectThisNode(true);
      });

      const event: any = {
        type: 'selectionChanged',
        api: this.params.api,
        columnApi: this.params.columnApi
      };

      this.params?.api?.eventService?.dispatchEvent(event);
    }
    else {
      this.params.api.deselectAll();
    }
  }

  onSortRequested(event: any): void {
    this.setSortIcon();
    this.params.progressSort(event.shiftKey);
  }

  refresh(params: IHeaderParams): boolean {
    return false;
  }

  private setCheckboxState(): void {
    this.isIndeterminate = false;
    const selectedNodesLength: number = this.params?.api?.getSelectedNodes()?.length;
    const renderedNodesLength: number = this.params?.api?.getDisplayedRowCount();

    this.isChecked = selectedNodesLength > 0 && selectedNodesLength === renderedNodesLength;
    this.isIndeterminate = selectedNodesLength > 0 && !this.isChecked;
  }

  private setDefaultSortIcon(): void {
    this.sortIcon = this.sortSubIcon = 'none';
  }

  private setSortIcon(): void {
    if(this.sortIcon === 'none' || this.sortIcon === 'descending'){
      this.sortIcon = 'ascending';
      this.sortSubIcon = 'asc';
    }
    else if(this.sortIcon === 'ascending'){
      this.sortIcon = 'descending';
      this.sortSubIcon = 'desc';
    }
  }
}

And in your html file:

select-all.component.html

  <mat-checkbox [ngModel]="isChecked"
                [indeterminate]="isIndeterminate"
                (change)="toggleSelect()">
  </mat-checkbox>


<span *ngIf="!params.enableSorting" 
    [class.with-checkbox]="showCheckBox">
    {{ headerName }}
</span> 

<div *ngIf="params.enableSorting" 
   (click)="onSortRequested($event)" 
   class="ag-cell-label-container">         
  <div class="ag-header-cell-label">
      <span [class.with-checkbox]="showCheckBox"
            class="ag-header-cell-text">
            {{ headerName }}
      </span>
      <span [ngClass]="'ag-sort-' + sortIcon + '-icon'"
            class="ag-header-icon">
          <span class="ag-icon"
                [ngClass]="'ag-icon-' + sortSubIcon">
          </span>
      </span>
  </div>
</div>

And in your app.component.ts you need to use this component by frameworkComponents like this:

 frameworkComponents: any = {
    selectAllComponent: SelectAllComponent,
  };
  columnDefs = [
    {
      field: 'make',
      checkboxSelection: true,
      sortable:true,
      headerComponent: 'selectAllComponent',
      headerComponentParams: {
        checkboxSelection: true,
      },
    },
    ...
  ];

And in your app.component.html file you have something like this:

<mat-checkbox [ngModel]="isChecked"
                [indeterminate]="isIndeterminate"
                (change)="toggleSelect()">
  </mat-checkbox>


<span *ngIf="!params.enableSorting" 
    [class.with-checkbox]="showCheckBox">
    {{ headerName }}
</span> 

<div *ngIf="params.enableSorting" 
   (click)="onSortRequested($event)" 
   class="ag-cell-label-container">         
  <div class="ag-header-cell-label">
      <span [class.with-checkbox]="showCheckBox"
            class="ag-header-cell-text">
            {{ headerName }}
      </span>
      <span [ngClass]="'ag-sort-' + sortIcon + '-icon'"
            class="ag-header-icon">
          <span class="ag-icon"
                [ngClass]="'ag-icon-' + sortSubIcon">
          </span>
      </span>
  </div>
</div>

Here is a working sample that I created from the scratch: StackBlitz

Jocundity answered 9/8, 2022 at 14:41 Comment(0)
A
2

Since [email protected] you can use headerCheckboxSelection: true also in SSRM :)

https://ag-grid.com/javascript-data-grid/server-side-model-selection/#header-checkbox-selection

Ashlieashlin answered 29/3, 2023 at 14:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.