angularangular-materialselectionmodel

previous data not getting selected after search in selectionmodel angular


I am working on angular selectionmodel where I have a search bar where user searches for list and selects items and if user closes the search keyword then previous selected values i.e., before search not getting selected in the selectionmodel list.

Search code in ts:

searchUsers(filterValue: string) {
  this.previousSelectedValues = this.userSelection.selected;
  console.log("came to searchUsers",filterValue);
  this.searchKey = filterValue;
  let params = { 'searchData': this.searchKey}
  this.commCenterService.getSearchedUsers(params).subscribe((res)=>{
    console.log("Search Response: Users",res);
    this.users = res;
    console.log("previous selected fields",this.userSelection.selected);
    if(filterValue == ""){
     this.previousSelectedValues.forEach(row => this.userSelection.select(row));
    }
    this.dataSource = new MatTableDataSource(this.users);
   });
}

selectionmodel initialization code:

userSelection = new SelectionModel<AddRecipientsList>(true, []);
/** Whether the number of selected elements matches the total number of rows. */
isAllSelected() {
  const numSelected = this.userSelection.selected.length;
  const numRows = this.dataSource.data.length;
  return numSelected === numRows;
}
/** Selects all rows if they are not all selected; otherwise clear selection. */
masterToggle() {
  this.isAllSelected() ?
  this.userSelection.clear() :
  this.dataSource.data.forEach(row => this.userSelection.select(row));
}
/** The label for the checkbox on the passed row */
checkboxLabel(row?: PeriodicElement): string {
 if (!row) {
  return `${this.isAllSelected() ? 'select' : 'deselect'} all`;
 }
}

HTML:

<div fxFlex="auto" fxLayoutAlign="start center" fxLayoutGap="10px">
  <mat-form-field fxFlex="100" appearance="outline">
    <input matInput type="text" (keydown.enter)="searchUsers(value)" [(ngModel)]="value">
    <mat-label fxLayoutAlign="start center">
     <mat-icon class="s-16">search</mat-icon>Search by Recipient Name, Email
    </mat-label>
    <button mat-button *ngIf="searchKey" matSuffix mat-icon-button aria-label="Clear" (click)="value=''; searchUsers(value);">
                <mat-icon class="s-16">close</mat-icon>
    </button>
 </mat-form-field>
</div>    

<div>
  <mat-table [dataSource]="dataSource" class="display-table">
    <!-- Checkbox Column -->
    <ng-container matColumnDef="select">
     <mat-header-cell *matHeaderCellDef>
      <mat-checkbox (change)="$event ? masterToggle() : null"
                  [checked]="userSelection.hasValue() && isAllSelected()"
                  [indeterminate]="userSelection.hasValue() && !isAllSelected()" [aria-label]="checkboxLabel()">
      </mat-checkbox>
     </mat-header-cell>>
     <mat-cell *matCellDef="let row">
       <mat-checkbox (click)="$event.stopPropagation()" (change)="$event ? userSelection.toggle(row) : null"
                  [checked]="userSelection.isSelected(row)" [aria-label]="checkboxLabel(row)">
       </mat-checkbox>
     </mat-cell>>
    </ng-container>
    <!-- recepientName Column -->
    <ng-container matColumnDef="recepientName">
     <mat-header-cell *matHeaderCellDef> recepient Name </mat-header-cell>
       <mat-cell *matCellDef="let element"> {{element.name}} </mat-cell>
    </ng-container>
    <!-- recepientEmail Column -->
    <ng-container matColumnDef="recepientEmail">
     <mat-header-cell *matHeaderCellDef> recepient Email </mat-header-cell>
       <mat-cell *matCellDef="let element"> {{element.email}} </mat-cell>
     </ng-container>
     <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
     <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
  </mat-table>
</div>

I am able to get my previous selected list from this.userSelection.selected

My selection/previous selected List is below:

previous selected fields 
0:
 email: "email1@example.com"
 name: "some name" 
1:
 email: "email2@example.com"
 name: "some name"

How does this work? Any Idea? Thanks.


Solution

  • SelectionModel holds references to objects. Therefore when you re-initialize this.dataSource in searchUsers function, references to actual objects in this.userSelection gets lost. In order to overcome this in your use case you have to re-initialize this.userSelection with new objects in new this.dataSource

    However, since your search returns different lists; it may not be possible to find previous user items in new search results. In this case i suggest using a primitive value (which acts as a unique id for user objects) for keeping elements in SelectionModel. Such that;

    selection.toggle(row.email) /** when selecting/de-selecting elements*/
    

    and

    selection.isSelected(row.email) /** when checking if an element is selected */
    

    Please note that; i suggest removig master toggle to select/unselect all elements because your list changes at every search and selecting/unselecting all in different lists seems a bit ambiguous in a user point of view.

    I also created a demo here;

    https://stackblitz.com/edit/angular-e9brcp

    in your html, update this part

    <mat-checkbox (click)="$event.stopPropagation()" (change)="$event ? userSelection.toggle(row) : null" 
        [checked]="userSelection.isSelected(row)" [aria-label]="checkboxLabel(row)">
    </mat-checkbox>
    

    to this

    <mat-checkbox (click)="$event.stopPropagation()" (change)="$event ? userSelection.toggle(row.email) : null" 
         [checked]="userSelection.isSelected(row.email)">
    </mat-checkbox>