74

I'm looking for a good way to highlight the whole a row in md-table.
Should I do directive or what?

<div class="example-container mat-elevation-z8">
  <md-table #table [dataSource]="dataSource">

    <!--- Note that these columns can be defined in any order.
          The actual rendered columns are set as a property on the row definition" -->

    <!-- ID Column -->
    <ng-container cdkColumnDef="userId">
      <md-header-cell *cdkHeaderCellDef> ID </md-header-cell>
      <md-cell *cdkCellDef="let row"> {{row.id}} </md-cell>
    </ng-container>

    <!-- Progress Column -->
    <ng-container cdkColumnDef="progress">
      <md-header-cell *cdkHeaderCellDef> Progress </md-header-cell>
      <md-cell *cdkCellDef="let row"> {{row.progress}}% </md-cell>
    </ng-container>

    <!-- Name Column -->
    <ng-container cdkColumnDef="userName">
      <md-header-cell *cdkHeaderCellDef> Name </md-header-cell>
      <md-cell *cdkCellDef="let row"> {{row.name}} </md-cell>
    </ng-container>

    <!-- Color Column -->
    <ng-container cdkColumnDef="color">
      <md-header-cell *cdkHeaderCellDef>Color</md-header-cell>
      <md-cell *cdkCellDef="let row" [style.color]="row.color"> {{row.color}} </md-cell>
    </ng-container>

    <md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
    <md-row *cdkRowDef="let row; columns: displayedColumns;"></md-row>
  </md-table>
</div>

Table from: https://material.angular.io/components/table/overview

ssuperczynski
  • 3,190
  • 3
  • 44
  • 61
Taison Morris
  • 878
  • 1
  • 8
  • 12

8 Answers8

103

Update for Newer Material Version (md --> mat):

html:

<!-- Add the highlight class in row definiton of md-table -->
<!-- Add click event to pass the selected row index -->

<mat-row *cdkRowDef="let row; columns: displayedColumns;" 
         [ngClass]="{'highlight': selectedRowIndex == row.id}"
         (click)="highlight(row)">
</mat-row>

Original Answer:

You can do it by using ngClass and a flag like selectedRowIndex. Whenever clicked row index is equal to selectedRowIndex, the class will be applied.

Plunker demo

html:

<!-- Add the highlight class in row definiton of md-table -->
<!-- Add click event to pass the selected row index -->

<md-row *cdkRowDef="let row; columns: displayedColumns;" 
         [ngClass]="{'highlight': selectedRowIndex == row.id}"
         (click)="highlight(row)">
</md-row>

css:

.highlight{
  background: #42A948; /* green */
}

ts:

selectedRowIndex = -1;

highlight(row){
    this.selectedRowIndex = row.id;
}
umutyerebakmaz
  • 887
  • 8
  • 18
Nehal
  • 13,130
  • 4
  • 43
  • 59
50

In the table overview examples page they explain the SelectionModel for handling selections - which incidentally also handles multi-selection.

selection is a SelectionModel defined in your component. I couldn't find any actual documentation for this but the implementation is extremely simple.

selection = new SelectionModel<CustomerSearchResult>(false, null);

The first parameter is allowMultiSelect, so to allow multiple items to be selected at once set it to true. When false the selection model will deselect any existing values when you set a new value.

Then add a click event to select() the row and create your own css class for when the row is selected.

   <mat-table>
        ...

        <mat-row *matRowDef="let row; columns: displayedColumns;"
                 [ngClass]="{ 'selected': selection.isSelected(row)}"
                 (click)="selection.select(row)"></mat-row>

    </mat-table>

The css class I added is below (the sample doesn't mention styling yet) and then you just need to add to your css

.mat-row {
   min-height: 65px;

   &.selected {
       background: #dddddd;
   }
}

If your background color is too dark you'll need to add styles yourself to invert the text color.

To handle selection use the onChange event of selection.

    // selection changed
    this.selection.onChange.subscribe((a) =>
    {
        if (a.added[0])   // will be undefined if no selection
        {
            alert('You selected ' + a.added[0].fullName);
        }
    });

Or alternatively the selected items are in this.selection.selected.

I'm hoping mat-table gets improved for common cases like this and they don't just leave everything up to the developer. Things like keyboard events etc. would be nice to be handled automatically with respect to the selection model.

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • 1
    If anyone finds proper documentation for SelectionModel please add a link. – Simon_Weaver Jan 07 '18 at 21:12
  • The issue with `[]` was confirmed to be a bug https://github.com/angular/material2/pull/9287 – Simon_Weaver Jan 08 '18 at 22:44
  • 6
    your's is the best answer. this question should be reviewed by community because the current marked answer isn't the best nor correct. – tatsu Feb 26 '18 at 13:39
  • 2
    **For people using this technique to select a single item in a master-detail type view**: I don't have time right now to add to this answer, but I strongly recommend looking into using the angular router and learning about child routes and outlets before using this technique too extensively in your application. I'm finding it much better to highlight a 'selected' row based on the current route than trying to mess with a separate selectionModel for master/detail situations. It works in a much more 'angular' way (eg. if you have unfinished data you can prevent navigation). – Simon_Weaver Mar 03 '18 at 00:07
  • 1
    you are the champion, great solution. – Brayan Loayza Nov 22 '18 at 21:56
  • Also note I've removed the reference in the answer to the referenced bug since it was fixed a long time ago now and it just adds unnecessary confusion leaving it here. – Simon_Weaver Feb 17 '19 at 02:03
  • Using SelectionModel is the way to go. – eddy Sep 30 '21 at 17:27
23

I did not have unique identifiers like id column in my table data but this worked for me (material 6):

HTML

 <tr mat-row *matRowDef="let row; columns: displayedColumns" 
     (click)="selectedRow = row" [ngClass]="{ 'selected': row === selectedRow }"> 
 </tr>

or HTML if you want to enable users to unselect on another click

 <tr mat-row *matRowDef="let row; columns: displayedColumns" 
     (click)="selectedRow = selectedRow === row ? null : row" [ngClass]="{ 'selected': row === selectedRow }"> 
 </tr>

add variable to TS

selectedRow;

(S)CSS

.selected {
  background-color: red;
}

If you want to do more things than just styling when selecting a row, replace (click)="selectedRow = row" with (click)="selectRow(row)" and add this function to your ts:

selectRow(row) {
    this.selectedRow = row;
    // more stuff to do...
}
J. S.
  • 143
  • 1
  • 7
Zuzze
  • 241
  • 2
  • 6
  • This answer worked best for me (using Angular 8), except it doesn't un-highlight when you click on the row a 2nd time... – noel Dec 02 '19 at 18:48
5

So, I ran into this issue as well. I'm using the newer 'mat-' instead of 'md-', but I'm GUESSING it will be about the same...

<mat-row
    *matRowDef="let row; columns: myColumns; let entry"
    [ngClass]="{ 'my-class': entry.someVal }">
</mat-row>

I didn't find that anywhere, I just tried it and it happened to work out, so I hope that's right. The big thing was tagging 'let entry' to the end of the other matRowDef stuff. Sorry I'm so late to the party. Hopefully this does someone some good.

4

For material": "^7.0.3",

use the css name in html, without the single quote, to highlight the row

 .mat-row.highlighted {
  background: lightblue;
  }


<tr mat-row *matRowDef="let row; columns: displayedColumn;" 
[ngClass]="{highlighted: selectedRowIndex == row.id}"  (click)="highlight(row)" > 
</tr>


highlight(row){
this.selectedRowIndex = row.id;
}
Priya
  • 67
  • 1
  • 5
1

Building on Zuzzie's answer, which is the solution that mostly worked for me, I did the following:

HTML:

<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;" 
    (click)="onRowClicked(row)" [ngClass]="{ 'selected': row === selectedRow }">
</mat-row>

add variable to TS:

selectedRow : boolean;

add this function to TS:

onRowClicked(row) {

   if(!this.selectedRow)
   {
     this.selectedRow = row;
   }   
   else
   {
     this.selectedRow = row;
   }

}

(S)CSS

.selected {
  background-color: red;
}
noel
  • 383
  • 4
  • 18
0

This will allow you to select multiple rows if the row is not previously selected and on clicking again it will deselect it.

HTML

<mat-row *matRowDef="let row; columns: displayedColumns;"
  (click)="findOut(row)"[style.background]="highlightedRows.indexOf(row) != -1 ? 'lightgrey' : ''"></mat-row>

Type Script

Create an array

highlightedRows = [];

Define the findOut function

findOut(row){
  if(this.highlightedRows.indexOf(row) === -1){
    this.highlightedRows.push(row);
    }
    else{
    
      this.highlightedRows[this.highlightedRows.indexOf(row)] = -1;
    }
    
  }
0

I have same request for all projects and I've created this directive:

import { Directive, ElementRef, HostListener, Input } from "@angular/core";
 
@Directive({
    selector: '[toggleActiveStyle]'
})
export class ToggleActiveStyleDirective {
    prevElement: HTMLElement;
 
    @Input() addActiveCSSClass: boolean;
 
    constructor(private el: ElementRef) {}
 
    @HostListener('click') onClick () {
        setTimeout(() => {
            for(let i = 0; i < this.el.nativeElement.parentElement.children.length; i++) {
                this.el.nativeElement.parentElement.children[i].style.background = '';
            }
            if(this.addActiveCSSClass) {
                this.el.nativeElement.style.background = 'white';
            }
            else {
                this.el.nativeElement.style.background = '#cccccc';
            }
        });
    }
}

And in HTML add:

And in component to can set: addBg = true/false;

Carnaru Valentin
  • 1,690
  • 17
  • 27