0

I have a table that a user can select two options, remove or retain. In the first column header I have a remove all and retain all radio button that will select every row in the dataSource. I want it to only select the rows that are displayed on the current page and not the entire dataSource. The paginator size right now is 5, but this will end up changing down the road.

StackBlitz: https://stackblitz.com/edit/angular-qkx3pc

Here is my HTML code:

<mat-drawer-container>
  <mat-drawer-content>
    <div class="tbl-container">
      <mat-progress-bar mode="query" color="accent" *ngIf="isLoading" mode="indeterminate"></mat-progress-bar>

      <table matSort mat-table [dataSource]="dataSource">
        <ng-container matColumnDef="header-row-first-group">
          <th mat-header-cell *matHeaderCellDef [style.text-align]="center" [attr.colspan]="2">
            Review Response
          </th>
        </ng-container>

        <ng-container matColumnDef="review">
          <mat-header-cell fxFlex="230px" *matHeaderCellDef>
            <mat-radio-button class="retain" (click)="retainAll()" style="padding-right: 5%;" [value]="0"
              >Retain All</mat-radio-button
            >
            <mat-radio-button class="remove" (click)="removeAll()" [value]="1" color="warn"
              >Remove All</mat-radio-button
            >
          </mat-header-cell>
          <mat-cell fxFlex="230px" *matCellDef="let item" (click)="$event.stopPropagation()">
            <mat-radio-group [value]="item.review">
              <mat-radio-button
                class="retain"
                [checked]="retain.checked"
                (change)="retain.checked = !retain.checked"
                [value]="0"
                >Retain</mat-radio-button
              >
              <mat-radio-button
                class="remove"
                [checked]="remove.checked"
                (change)="retain.checked = !retain.checked"
                style="margin-left: 20px;"
                color="warn"
                [value]="1"
                >Remove</mat-radio-button
              >
            </mat-radio-group>
          </mat-cell>
        </ng-container>

        <ng-container matColumnDef="mots_id">
          <mat-header-cell *matHeaderCellDef mat-sort-header>Mots ID</mat-header-cell>
          <mat-cell *matCellDef="let item">{{ item.mots_id }}</mat-cell>
        </ng-container>

        <ng-container matColumnDef="completed">
          <mat-header-cell *matHeaderCellDef mat-sort-header>Completed</mat-header-cell>
          <mat-cell *matCellDef="let item">{{ item.completed }}</mat-cell>
        </ng-container>

        ...

        <tr mat-header-row *matHeaderRowDef="['header-row-first-group']"></tr>
        <mat-header-row *matHeaderRowDef="displayedColumns; sticky: true" class="tbl-header"></mat-header-row>

        <mat-row
          *matRowDef="let row; columns: displayedColumns"
          (click)="helpdrawer.open(); selectedRow(row); highlight(row)"
          [ngClass]="{ highlight: selectedRowIndex == row.uid }"
        ></mat-row>
      </table>
      ...
    </div>
    ...
  </mat-drawer-content>
  ...
</mat-drawer-container>

Here is my TS Code:

class Component {
  @ViewChild(MatPaginator) gridPaginator: MatPaginator;
  @ViewChild(MatSort) gridSort: MatSort;
  @ViewChild('ref') ref: any;

  gridSettings = [
    { primaryKey: 'review', header: 'Review Response', render: 'both' },
    { primaryKey: 'mots_id', header: 'MOTS ID', render: 'both' },
    { primaryKey: 'app', header: 'App', render: 'both' },
    { primaryKey: 'env', header: 'Environment', render: 'both' },
    { primaryKey: 'profile', header: 'Profile', render: 'both' },
    { primaryKey: 'account_id', header: 'Account ID', render: 'both' },
    { primaryKey: 'owner_id', header: 'Owner ID', render: 'both' },
    { primaryKey: 'ccdr_read_only', header: 'Read Only', render: 'both' },
    { primaryKey: 'sec_layer', header: 'Security Layer', render: 'both' },
    { primaryKey: 'sec_image_id', header: 'Security Image ID', render: 'both' },
    { primaryKey: 'instance', header: 'Instance', render: 'both' },
    { primaryKey: 'data_source', header: 'DataSource', render: 'both' },
    { primaryKey: 'spi', header: 'SPI', render: 'both' },
    { primaryKey: 'mots_sox_indicator', header: 'SOX', render: 'both' },
  ];

  dataSource = new MatTableDataSource<any>();
  selection = new SelectionModel<any>(true, []);
  displayedColumns = this.gridSettings.filter(a => a.render != 'export').map(b => b.primaryKey);

  retain = {
    checked: false,
  };

  remove = {
    checked: false,
  };

  constructor(
    private cdRef: ChangeDetectorRef,
    private accessReviewDataService: AccessReviewDataService,
    private userService: UserService,
    public dialog: MatDialog,
  ) {
    this.currentUser = this.userService.currentUser();
  }

  ngAfterContentInit() {
    // init sort
    this.gridSort.sort({ id: 'mots_id', start: 'desc', disableClear: false });
    this.dataSource.sort = this.gridSort;

    // init paging
    this.dataSource.paginator = this.gridPaginator;
    //this.cdRef.detach();

    this.subscriptions.add(
      this.accessReviewDataService.getAccessReview(this.currentUser.user_id).subscribe(
        results => {
          this.dataSource.data = results;
          this.addId(results);
          this.isLoading = false;
        },
        error => (this.isLoading = false),
      ),
    );
  }

  retainAll() {
    this.remove.checked = false;
    this.retain.checked = true;
  }

  removeAll() {
    this.retain.checked = false;
    this.remove.checked = true;
  }
}

Right now it selects every row in the table, I only want it to select the rows that are being displayed on the page. Any help/direction would be appreciated. Thanks!

  • can you create a stackblitz example for this. – Storytellerr Feb 03 '20 at 19:30
  • This requires a service that is getting an id from the backend, and the data is coming from the backend so I cant post it on stackblitz – Eduardo Siguenza Feb 03 '20 at 19:38
  • mock data with array of objects – Storytellerr Feb 03 '20 at 19:39
  • Ok, @shashanksharma I have created a stackblitz version of my project here: https://stackblitz.com/edit/angular-qkx3pc When you hit either retain all or remove all and click on the next page, it selects the entire dataSource and not the data displayed on the current page. How can I add logic for this? – Eduardo Siguenza Feb 03 '20 at 21:36

2 Answers2

0

You can follow this logic that i have made :

This may help you a lot.

EXPLANATION : i have looped over your data to add a retain property to control the checked value for each item

TS Code :

import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import {MatTableDataSource} from '@angular/material/table';
import {SelectionModel} from '@angular/cdk/collections';
import { Subscription } from 'rxjs';
import {MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogContent} from '@angular/material/dialog';
import { ChangeDetectorRef } from '@angular/core';

export interface AppData {
    
  id: any;
  completed: boolean;
  mots_spi_indicator: string;
  mots_sox_indicator: string;
  mots_pci_indicator: string;
  ml_spi_indicator: string;
  ml_pci_indicator: string;
  ml_read_only: string;
  ml_privileged_access: string;
  ml_profile_description: string;
  ml_authorized_jobfunction: string;
  ml_unauthorized_jobfunction: string;
  ml_financial_type: string;
  ml_profile_label: string;
  ccdr_spi_indicator: string;
  ccdr_normal_user_population: string;
  ccdr_read_only: string;
  ccdr_modify_user_access: string;
  owner_mgt_indicator: string;
  owner_level_indicator: number;
  owner_title: string;
  supervisor_mgt_indicator: string;
  supervisor_level_indicator: number;
  supervisor_title: string;
  spi: string;
  data_source: string;
  mots_id: number;
  app: string;
  profile: string;
  env_id: number;
  env: string;
  sec_layer_id: number;
  sec_layer: string;
  sec_image_id: number;
  sec_image: string;
  instance: string;
  account_id: string;
  owner_id: string;
  owner: string;
  sponsor_id: string;
  sponsor: string;
  status: string;
  supervisor_id: string;
  supervisor: string;
  run_date: string;
  chain_of_command_attuid: string;
  chain_of_command_last_name: string;
  
}

const APPS: AppData[] = []; // <== fill your data here

/**
 * @title Table with selection
 */
@Component({
  selector: 'table-selection-example',
  styleUrls: ['table-selection-example.css'],
  templateUrl: 'table-selection-example.html',
})

  

export class TableSelectionExample {
  @ViewChild(MatPaginator, ({static: true})) gridPaginator: MatPaginator;

  gridSettings = [
    {primaryKey: 'review', header: 'Review Response', render: 'both'},
    {primaryKey: 'mots_id', header: 'MOTS ID', render: 'both'},
    {primaryKey: 'app', header: 'App', render: 'both'},
    {primaryKey: 'env', header: 'Environment', render: 'both'},
    {primaryKey: 'profile', header: 'Profile', render: 'both'},
    {primaryKey: 'account_id', header: 'Account ID', render: 'both'},
    {primaryKey: 'owner_id', header: 'Owner ID', render: 'both'},
    {primaryKey: 'ccdr_read_only', header: 'Read Only', render: 'both'},
    {primaryKey: 'sec_layer', header: 'Security Layer', render: 'both'},
    {primaryKey: 'sec_image_id', header: 'Security Image ID', render: 'both'},
    {primaryKey: 'instance', header: 'Instance', render: 'both'},
    {primaryKey: 'data_source', header: 'DataSource', render: 'both'},
    {primaryKey: 'spi', header: 'SPI', render: 'both'},
    {primaryKey: 'mots_sox_indicator', header: 'SOX', render: 'both'},
  ]

    dataSource = new MatTableDataSource<AppData>(APPS);
    selection = new SelectionModel<AppData>(true, []);
    displayedColumns = this.gridSettings.filter( a => a.render != 'export').map( b => b.primaryKey );
    
    retain = {
    checked: false
  }

  remove = {
    checked: false
  }

  constructor(){
  }

  ngOnInit() {
      for(let i = 0; i< APPS.length; i++) {
        APPS[i]["retain"] = true;
      }
  }

    ngAfterContentInit() {

    // init paging
    this.dataSource.paginator = this.gridPaginator;
  }

    retainAll(){
    const itemsCount = this.dataSource.paginator.pageIndex * this.dataSource.paginator.pageSize;
    let itemsTotal = (this.dataSource.paginator.pageIndex+1) * this.dataSource.paginator.pageSize;
    // in case you are in pageIndex 1 and it only has one row, to avoid looking into an index outofbounds 
    itemsTotal = itemsTotal > this.dataSource.data.length ? 
      this.dataSource.data.length : itemsTotal;
    for(let i = itemsCount; i < itemsTotal; i++) {
      APPS[i]["retain"] = true;
    }
  }

  removeAll(){
    const itemsCount = this.dataSource.paginator.pageIndex * this.dataSource.paginator.pageSize;
    let itemsTotal = (this.dataSource.paginator.pageIndex+1) * this.dataSource.paginator.pageSize;
    itemsTotal = itemsTotal > this.dataSource.data.length ? 
      this.dataSource.data.length : itemsTotal;
    for(let i = itemsCount; i < itemsTotal; i++) {
      APPS[i]["retain"] = false;
    }
  }
}

Then HTML Code :

<div>
  <mat-paginator #gridPaginator [pageSizeOptions]="[5]"></mat-paginator>
</div>

<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">

  <!-- Checkbox Column -->

  <ng-container matColumnDef="review">
                    <th mat-header-cell *matHeaderCellDef>
                        <mat-radio-button class="retain" (click)="retainAll()" [value]="0">Retain All</mat-radio-button>
                        <mat-radio-button class="remove" (click)="removeAll()" [value]="1" color="warn">Remove All</mat-radio-button>
                    </th>
                    <td mat-cell *matCellDef="let item" (click)="$event.stopPropagation();">
                        <mat-radio-group [value]="item.retain">
                            <mat-radio-button class="retain" [checked]="item.retain" (change)="item.retain = true" [value]="0">Retain</mat-radio-button>
                            <mat-radio-button class="remove" [checked]="!item.retain" (change)="item.retain = false" color="warn" [value]="1">Remove</mat-radio-button>
                        </mat-radio-group>
                    </td>
        </ng-container>

  <ng-container matColumnDef="mots_id">
                    <th mat-header-cell *matHeaderCellDef >Mots ID</th>
                    <td mat-cell  *matCellDef="let item">{{item.mots_id}}</td>
                    </ng-container>
                  
                  
                    <ng-container matColumnDef="app">
                        <th mat-header-cell *matHeaderCellDef >App</th>
                        <td mat-cell  *matCellDef="let item">{{item.app}}</td>
                    </ng-container>
                  
                    <ng-container matColumnDef="env">
                    <th mat-header-cell *matHeaderCellDef >ENV</th>
                    <td mat-cell  *matCellDef="let item">{{item.env}}</td>
                    </ng-container>
                  
                    <ng-container matColumnDef="profile">
                    <th mat-header-cell *matHeaderCellDef >Profile</th>
                    <td mat-cell *matCellDef="let item">{{item.profile}}</td>
            </ng-container>
    <ng-container matColumnDef="account_id">
                    <th mat-header-cell *matHeaderCellDef >Account ID</th>
                    <td mat-cell  *matCellDef="let item">{{item.account_id}}</td>
                    </ng-container>
                  
                    <ng-container matColumnDef="owner_id">
                    <th mat-header-cell *matHeaderCellDef >Owner ID</th>
                    <td mat-cell  *matCellDef="let item">{{item.owner_id}}</td>
                    </ng-container>
                  
                    <ng-container matColumnDef="ccdr_read_only">
                    <th mat-header-cell *matHeaderCellDef >Read Only</th>
                    <td mat-cell  *matCellDef="let item">{{item.ccdr_read_only}}</td>
                    </ng-container>    
                  
                    <ng-container matColumnDef="sec_layer">
                    <th mat-header-cell *matHeaderCellDef >Security Layer</th>
                    <td mat-cell  *matCellDef="let item">{{item.sec_layer}}</td>
                    </ng-container>    
                  
                    <ng-container matColumnDef="sec_image_id">
                    <th mat-header-cell *matHeaderCellDef >Security Image ID</th>
                    <td mat-cell  *matCellDef="let item">{{item.sec_image}} / {{item.sec_image_id}}</td>
                    </ng-container>   
                  
                    <ng-container matColumnDef="instance">
                    <th mat-header-cell *matHeaderCellDef >Instance</th>
                    <td mat-cell  *matCellDef="let item">{{item.instance}}</td>
                    </ng-container>
                  
                    <ng-container matColumnDef="data_source">
                    <th mat-header-cell *matHeaderCellDef >Datasource</th>
                    <td mat-cell  *matCellDef="let item">{{item.data_source}}</td>
                    </ng-container>
                  
                    <ng-container matColumnDef="spi">
                    <th mat-header-cell *matHeaderCellDef >SPI</th>
                    <td mat-cell  *matCellDef="let item">{{item.spi}}</td>
                    </ng-container>
                  
                    <ng-container matColumnDef="mots_sox_indicator">
                    <th mat-header-cell *matHeaderCellDef >SOX</th>
                    <td mat-cell *matCellDef="let item">{{item.mots_sox_indicator}}</td>
                    </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"
      (click)="selection.toggle(row)">
  </tr>
</table>

Cheers.

Pepe Alvarez
  • 1,218
  • 1
  • 9
  • 15
Oussail
  • 2,200
  • 11
  • 24
0

I've modified your Stackblitz to make it into what you wanted. There are a few things that you need to do here.

  1. [checked]="retain.checked" is a problem since that gets applied to all the items in the table.
  2. Give a unique id to Apps objects.
  3. On click on the radio button, you know the page size and get the page index that way you can determine what objects were selected i.e. the ones that are being displayed.

I've implemented it for Retain All, I'm sure you can replicate this for Romove All.

Stackblitz link : https://stackblitz.com/edit/angular-kifbgs

CodeWarrior
  • 5,026
  • 6
  • 30
  • 46