TomSelleck
TomSelleck

Reputation: 6968

Unable to expand mat row

I'm trying to display a table and expand the rows when they are clicked to show some additional information.

I have the following HTML / TS displaying the table without issue, but when I add the connect code - the rows don't react on click and rows is printed as "[]" in the console..

Html:

<div class="example-container mat-elevation-z8">
  <mat-table #table [dataSource]="dataSource">
    <!-- Position Column -->
    <ng-container matColumnDef="Name">
      <mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
      <mat-cell *matCellDef="let apptestlist"> {{apptestlist.Name}} </mat-cell>
    </ng-container>

    <!-- Name Column -->
    <ng-container matColumnDef="AppCount">
      <mat-header-cell *matHeaderCellDef> Application Count </mat-header-cell>
      <mat-cell *matCellDef="let apptestlist"> {{apptestlist.Apps.length}} </mat-cell>
    </ng-container>

    <!-- Expanded Content Column - The detail row is made up of this one column -->
    <ng-container matColumnDef="expandedDetail">
      <mat-cell *matCellDef="let apptestlist"> 
        The symbol for {{apptestlist.Name}} is Banana
      </mat-cell>
    </ng-container>

    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;"
            matRipple 
            class="element-row" 
            [class.expanded]="expandedElement == row"
            (click)="expandedElement === row? expandedElement = null : expandedElement = row"></mat-row>
    <mat-row *matRowDef="let row; columns: ['expandedDetail']; when: isExpansionDetailRow"
            [@detailExpand]="row.element == expandedElement ? 'expanded' : 'collapsed'"
            style="overflow: hidden"> 
    </mat-row>
  </mat-table>
</div>

TS:

import { Component, OnInit } from '@angular/core';
import { AppTestListService } from '../services/app-test-list-service'
import { AppTestList } from '../models/AppTestList';
import { DataSource } from '@angular/cdk/table';
import { Observable, of } from 'rxjs';
import { animate, state, style, transition, trigger } from '@angular/animations';

@Component({
  selector: 'app-application-test-lists',
  templateUrl: './application-test-lists.component.html',
  styleUrls: ['./application-test-lists.component.css'],
  animations: [
    trigger('detailExpand', [
      state('collapsed', style({ height: '0px', minHeight: '0', visibility: 'hidden' })),
      state('expanded', style({ height: '*', visibility: 'visible' })),
      transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
    ]),
  ],
})

export class ApplicationTestListsComponent implements OnInit {

  constructor(private appTestListService: AppTestListService) { }

  dataSource = new AppTestListDataSource(this.appTestListService);
  displayedColumns = ['Name', 'AppCount'];
  appTestLists: AppTestList[];
  loading = true;
  isExpansionDetailRow = (i: number, row: Object) => row.hasOwnProperty('detailRow');
  expandedElement: any;

  ngOnInit() {
  }
}

export class AppTestListDataSource extends DataSource<any> {
  constructor(private appTestListService: AppTestListService) {
    super();
  }
  connect(): Observable<AppTestList[]> {
    return this.appTestListService.list();
  }
  disconnect() {}
}

This renders nicely how I want it:

enter image description here

I want to be able to expand the rows and display some additional information e.g

<ng-container matColumnDef="expandedDetail">
  <mat-cell *matCellDef="let apptestlist"> 
    The symbol for {{apptestlist.Name}} is Banana
  </mat-cell>
</ng-container>

However it's not working..

I've changed my connect method to the following but it appears to return before any changes have happened:

  connect(): Observable<AppTestList[]> {
    const rows = [];
    let data = this.appTestListService.list();
    data.forEach(element => rows.push(element, { detailRow: true, element }));
    console.log(rows);
    return of(rows);
  }

list():

  public list(): Observable<AppTestList[]> {
    return this.httpClient
      .get<AppTestList[]>(`${this.url}/${this.endpoint}`);
  }

Upvotes: 0

Views: 978

Answers (1)

Marshal
Marshal

Reputation: 11081

Because AppTestListService.list() returns an Observable with or without the records initially, it is being assigned to your data variable and your forEach, console.log and return of(rows) completes before your .get is finished retrieving the records.

You will need to wrap that part in a subscribe to ensure that it happens when you know you have the data from the .get

Also Note:

{{apptestlist.Name}} does not mean anything to your detail row, you may want {{apptestlist.element.Name}} instead.

connect(): Observable<AppTestList[]> {
       const rows = [];
       this.appTestListService.list().subscribe(val=>{
         val.forEach(element => rows.push(element, { detailRow: true, element }));
       })
       return of(rows);
     }

Stackblitz

https://stackblitz.com/edit/angular-golnkq?embed=1&file=app/table-basic-example.html

Upvotes: 1

Related Questions