15

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?

0

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Can you please provide an example on how did you create this manually? Thanks!
4

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

1 Comment

Thanks you! I implemented it slightly differently, but I thought using ag-grid styled checkbox rather than material checkbox worked better with ag-grid's theming, and was worth sharing (of course, it creates more of a dependency) <div class="ag-wrapper ag-input-wrapper ag-checkbox-input-wrapper" [ngClass]="{'ag-checked':isChecked,'ag-indeterminate':isIndeterminate}"> <input class="ag-input-field-input ag-checkbox-input" type="checkbox" (change)="toggleSelect()" [ngModel]="isChecked"> </div>
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.