Is it possible to only select items within the current PageSize in Mat-Table?

I am currently developing a table with pagination that involves performing certain actions based on an array of selected checkboxes. I have referred to the documentation and everything seems to be working fine when it comes to selecting individual rows or all rows;

However, I am facing an issue with the functionality I want to achieve. When my PageSize is set to "10" and I use the masterToggle to select all rows, I only want the selection to apply to the 10 rows displayed on the current page, not the entire dataset which contains around 300 records.

Is there a way to modify the behavior of the masterToggle so that it selects only the rows visible based on the PageSize setting? For example, if I switch the PageSize to 20, only the first 10 rows should remain selected.

Below is the relevant section of my code:

/** Checks if all elements are selected. */
  isAllSelected() {
    const numSelected = this.selection.selected.length;
    const numRows = this.dataSource.data.length;
    return numSelected === numRows;
  }

  /** Selects all rows if they are not all selected; clears selection otherwise. */
  masterToggle() {
    this.isAllSelected() ?
        this.selection.clear() :
        this.dataSource.data.forEach(row => this.selection.select(row));
  }

Answer №1

If you are utilizing sorting, pagination, and filtering:

Html column section:

<mat-checkbox (change)="$event ? masterToggle($event) : null"
    [checked]="selection.hasValue() && isEntirePageSelected()"
    [indeterminate]="selection.hasValue() && !isEntirePageSelected()"
    [aria-label]="checkboxLabel()"></mat-checkbox>

Component part:

  getPageData() {
    return this.dataSource._pageData(this.dataSource._orderData(this.dataSource.filteredData));
  }

  isEntirePageSelected() {
    return this.getPageData().every((row) => this.selection.isSelected(row));
  }

  masterToggle(checkboxChange: MatCheckboxChange) {
    this.isEntirePageSelected() ?
      this.selection.deselect(...this.getPageData()) :
      this.selection.select(...this.getPageData());
  }

  checkboxLabel(row): string {
    if (!row) {
      return `${this.isEntirePageSelected() ? 'select' : 'deselect'} all`;
    }
    return `${this.selection.isSelected(row) ? 'deselect' : 'select'} row ${row.id + 1}`;
  }

Answer №2

After some troubleshooting, I found a solution by modifying the masterToggle() method in a different way. Instead of removing the last line, I created a custom function that iterates through the PageSize of the paginator and calls the select method of the

SelectionModel</code for each item it encounters. Additionally, I adjusted the logic in the <code>isAllSelected
method to compare the number of selected items to the PageSize instead of comparing them to the entire dataSource.

/** Checks if all elements are selected. */
  isAllSelected() {
    const numSelected = this.selection.selected.length;
    const page = this.dataSource.paginator.pageSize;
    return numSelected === page;
  }

  /** Toggles between selecting all rows or clearing selection based on current state. */
  masterToggle() {
    this.isAllSelected() ? 
    this.selection.clear() : this.selectRows();
  }

  selectRows() {
    for (let index = 0; index < this.dataSource.paginator.pageSize; index++) {
      this.selection.select(this.dataSource.data[index]);
      this.selectionAmount = this.selection.selected.length;
    }
  }

Answer №3

My approach is focused on the currently visible rows, ensuring functionality with pagination in mind. There are areas for enhancement so feel free to suggest improvements.

  checkIfAllSelected() {

    const numSelected = this.selection.selected.length;
    const displayedRows = this.filteredEvents.connect().value.length;
    let isAllSelected = (numSelected === displayedRows);

    if (isAllSelected) {
      isAllSelected = this.checkIfAllDisplayedSelected();
    }

    return isAllSelected;
  }

  checkIfAllDisplayedSelected() {
    let isAllDisplayedSelected = true;

    if (this.selection.selected.length === 0) {
      return this.checkIfAllSelected();
    }

    this.filteredEvents.connect().value.some(element => {
      if (!this.selection.isSelected(element)) {
        isAllDisplayedSelected = false;
        return isAllDisplayedSelected;
      }
    });
    return isAllDisplayedSelected;
  }

  toggleSelection() {
    this.isViewableSelected() ?
      this.deselectItems() :
      this.selectItems();
  }

  isViewableSelected() {
    return (this.checkIfAllSelected() || this.checkIfAllDisplayedSelected());
  }

  deselectItems() {
    const itemsToBeUnselected = this.filteredEvents.connect().value;
    itemsToBeUnselected.forEach(element => {
      this.selection.deselect(element);
    });
  }

  selectItems() {
    const currentlyDisplayedRows = this.filteredEvents.connect().value;

    for (let index = 0; index < currentlyDisplayedRows.length; index++) {
      this.selection.select(currentlyDisplayedRows[index]);
      this.selectedAmount = this.selection.selected.length;
    }
  }

The HTML:

<ng-container matColumnDef="select">
  <th mat-header-cell *matHeaderCellDef>
    <mat-checkbox (change)="$event ? toggleSelection() : null" [checked]="selection.hasValue() && isViewableSelected()"
      [indeterminate]="selection.hasValue() && !isViewableSelected()">
    </mat-checkbox>
  </th>
  <td mat-cell *matCellDef="let row">
    <mat-checkbox (click)="$event.stopPropagation()" (change)="$event ? selection.toggle(row) : null"
      [checked]="selection.isSelected(row)">
    </mat-checkbox>
  </td>
</ng-container>

Answer №4

In this solution, I have implemented an additional if switch to uncheck the page.

Here is the HTML code for the column:

<mat-checkbox #ref
       (click)="$event.stopPropagation()"
       (change)="$event ? masterToggle(ref) : null"></mat-checkbox>

HTML cell:

<mat-checkbox (click)="$event.stopPropagation()"
                    [checked]="selection.isSelected(element)"></mat-checkbox>

For the HTML paginator:

<mat-paginator fixed [pageSizeOptions]="[20, 50, 100]" [pageSize]="20" (page)="pageChange()" showFirstLastButtons></mat-paginator>

The component logic includes the following:

@ViewChild('ref') ref: any;

masterToggle(_ref: any) {
    if(_ref.checked){
        this.checkPageOnly();
    }
    else {
        this.selection.clear();
    }
}

checkPageOnly() {
      let i = this.dataSource.paginator.pageIndex * this.dataSource.paginator.pageSize;
      let end = (this.dataSource.paginator.pageIndex + 1) * this.dataSource.paginator.pageSize;
      for(i; i < end; i++) {
        this.selection.select(this.dataSource.data[i]);
      }
}

pageChange() {
    this.ref.checked = false;
    this.masterToggle(this.ref);
}

Answer №5

I discovered a more efficient approach for handling this task with Angular:
Instead of using this.dataSource.data.length, you can replace it with

this.dataSource.connect().value.forEach(row => this.selection.select(row));

It would look something like this:

  masterToggle() {
    this.isAllSelected() ?
    this.selection.clear() :
    this.dataSource.connect().value.forEach(row => this.selection.select(row));
  }

The only limitation here is that it may not clear all the selected fields; to address this, modify your isAllSelected() method as follows:

  isAllSelected() {
    const numSelected = this.selection.selected.length;
    const numRows = this.dataSource.paginator != undefined ? this.dataSource.paginator.pageSize : 5;
    return numSelected === numRows;
 }

In this context, "5" represents your initial page size, set until the dataSource is initialized.

Answer №6

          checkIfAllSelected() {
              return this.selection.selected.length > 0;
            }

            /** Selects all rows if they are not all selected; otherwise clear selection. */
            toggleSelection() {
              this.checkIfAllSelected() ?
                this.selection.clear() :
                this.selectRows();
            }
            
            changePage(event) {
              this.selection.clear();
            }

            selectRows() {
              const startIndex = this.dataSource.paginator.pageIndex === 0 ? 0 : (this.dataSource.paginator.pageSize * this.dataSource.paginator.pageIndex);
              let endIndex = startIndex + this.dataSource.paginator.pageSize;
              
              if (endIndex > this.dataSource.filteredData.length) {
                endIndex = this.dataSource.filteredData.length;
              }
              
              for (let index = startIndex; index < endIndex; index++) {
                this.selection.select(this.dataSource.filteredData[index]);
              }
            }

            /** The label for the checkbox on the passed row */
            updateCheckboxLabel(row?: TicketMatchingElement): string {
              if (!row) {
                return `${this.checkIfAllSelected() ? 'select' : 'deselect'} all`;
              }
              return `${this.selection.isSelected(row) ? 'deselect' : 'select'} row ${row.bookingId + 1}`;
            }

            formatValue(dateValue: Date) {
              return formatDate(dateValue);
            }
          }

HTML with pagination changes:

<mat-paginator [pageSizeOptions]="[5, 10, 20,25]"
        showFirstLastButtons (page)="changePage($event)"
      ></mat-paginator>

Answer №7

Just stumbled upon this solution

To retrieve all the items currently displayed in your table, you can utilize datasource.connect().value.

Therefore, based on your code snippet, you could achieve the following:

masterToggle() {
    this.isAllSelected() ?
        this.selection.clear() :
        this.dataSource.connect().value.forEach(row => this.selection.select(row));
  }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

A guide to implementing angularjs app.service and $q in typescript

I am fairly new to TypeScript and AngularJS and I am struggling to find the correct answer for my issue. Below is the relevant code snippet: export class SidenavController { static $inject = ['$scope', '$mdSidenav']; constructor(p ...

Defining a TypeScript structure for a JSON object by referencing another entry within the same object

I'm currently working on defining a TypeScript structure for a JSON object. One part of the object includes a property called components, which is an array of strings. However, I want to enhance this structure by adding an additional property called o ...

The installed NPM package does not contain the necessary TypeScript compiled JS files and declaration files

I have recently released a TypeScript library on NPM. The GitHub repository's dist (Link to Repository Folder) directory includes all compiled JavaScript and d.ts files. However, after running npm i <my_package>, the resulting module contains on ...

Styling and theming in PrimeNG

Seeking advice on how to customize the appearance of the background for the primeNG panel component. I attempted to override the styling using the specific names in my scss file for the component, but it did not work. I also tried inline with <p-panel ...

Creating classes dynamically in Angular2 and Ionic2

Dealing with a dilemma here. I am attempting to dynamically create an instance of a class from its superclass. Essentially, I have an AbstractClass and multiple classes that inherit from it. Check out this functional example in the TypeScript playground : ...

Activate the code when any of the observables changes

I have a few observables within my state service that are generated from a behavior subject: _state.sortModels$.subscribe(sortModels => { //do something }); _state.filterModel$.subscribe(filterModel => { //do something }); I am looking to ...

"Troubleshooting the issue of Angular's ViewContainerRef.createComponent malfunctioning on subsequent

My application is structured into Header, Context, and Content sections. I am looking to dynamically inject different components into the Context Component (viewContainerRef) based on the route and other conditions. To achieve this functionality, I have ...

How can one restrict the display of fields in the Meteor aldeed tabular package?

How can I restrict certain data from being displayed in an aldeed tabular datatable? For instance, if my collection includes attributes A, B, C, D and attribute C contains sensitive information that should not be published, is there a way to prevent it fro ...

Quick way to specify type for Observable in Typescript

Exploring Shortcut Declarations When working with TypeScript, I often take a shortcut when declaring object shapes. Instead of creating an interface first and then specifying that the object conforms to that type, I simply do: object: { fizz: boolean, buz ...

The RC-dock library's 'DockLayout' is not compatible with JSX components. The instance type 'DockLayout' is not a valid JSX element and cannot be used as such

Despite encountering similar questions, none of the provided answers seem to address the issue within my codebase. My project utilizes React 17, Mui v5, and TS v4. I attempted to integrate a basic component from an external package called rc-dock. I simply ...

A guide on how to define prop types for mobx using TypeScript, React, and mobx

I have a fully functional root component structured like this const RootPage: React.FC = () => { const classes = useStyles(); return ( <React.Fragment> <Sidebar/> <Grid container className={classe ...

When trying to use TypeScript with next.js, encountering an unexpected token `?` error is not

Having an issue with next.js, the command npm run dev keeps failing due to a syntax error related to an optional property in a tsx file: Syntax error: Unexpected token 44 | 45 | type State<T_HT> = { > 46 | ghostHighlight: ?{ | ...

Storing a reference globally in React and Typescript: Best practices

In my application, I have multiple instances of a specific component called <Item>. Each <Item> needs to display a dynamic tooltip when hovered over. To achieve this, I am utilizing semantic-ui-react and its Popup component. The conventional m ...

There was an issue encountered when creating the class: The parameters provided do not correspond to any valid call target signature

I am facing an issue with my code. Here is the scenario: export class MyClass { public name:string; public addr:string; constructor() {} } I have imported MyClass and trying to use it like this: import { MyClass } from './MyClass' ...

Directive for Angular 2: Expand Further

Looking to create a custom readmore directive in Angular2 that will collapse and expand long blocks of text based on a specified max height, rather than character count. The directive will include "Read more" and "Close" links. <div read-more [maxHeigh ...

Creating synchronization mechanisms for events in JavaScript/TypeScript through the use of async/await and Promises

I have a complex, lengthy asynchronous process written in TypeScript/JavaScript that spans multiple libraries and functions. Once the data processing is complete, it triggers a function processComplete() to indicate its finish: processComplete(); // Signa ...

Calculating Events with the onChange Method in Typescript

How do I calculate the total ticket price when I adjust the number of individuals? HTML Code : <div class="row"> <div class="col-md-6"> <label for="person">Person</label> <div class="form-group"> ...

Transferring the click functionality between components in Angular

I have a parent component called Component1. In its HTML, I've included the following code: <div> <router-outlet></router-outlet> </div> <div> <button>Banner</button> </div> My goal is to pass the f ...

What is the best way to trigger a click event on an input field inside a mat-form-field

I need to reset the selected option when clicking on the autocomplete button: <mat-form-field class="example-full-width" (click)="clear()" appearance="outline"> <input matInput placeholder="State" aria-label="State" [matAutocomplete]=" ...

Local variables are now being refreshed with every modification in the data stored in Cloud Firestore

Having trouble maintaining the accuracy of my local variables in sync with changes to the data in cloud firestore. Specifically, in my local variable called count_vehicle, the value represents a count based on specific conditions from the data in cloud fir ...