Angular MatTable Nested Table Not Displaying

59 Views Asked by At

I'm working on an Angular project where I have a MatTable, and I want to display a nested table when clicking the "Add" button (toggle button). However, the nested table is not displaying as expected.

Code Snippets:

This is my html file

<div class="mainHeader text-center">Search By OUID</div>
<div class="mainContainer">
    <div class="container">

        <form #ouidForm="ngForm">
            <div class="form-group">
                <label for="searchouid" class="labelstyle">Enter OUID</label>
                <div class="search-container d-flex align-items-start">
                    <div class="input-group">
                        <input #searchInput="ngModel" type="text" class="form-control" id="searchouid"
                            aria-label="Search by OUID" placeholder="Please Enter OUID" [(ngModel)]="ouid" name="ouid"
                            required [class.is-invalid]="searchInput.invalid && searchInput.touched">
                        <div *ngIf="searchInput.touched && searchInput.invalid" class="invalid-feedback">
                            OUID is Required
                        </div>
                    </div>
                    <button (click)="searchOUID(searchInput.value, $event)" class="btnRounded"
                        [disabled]="searchInput.invalid || isSearching">Search</button>
                </div>
            </div>
        </form>


    </div>

    <div *ngIf="showForm">
        <hr>
        <div class="refreshbutton">
            <button type="button" class="btnOutlined" [disabled]="dataSource.data.length <= 1" (click)="splitData()">
                <span class="bi bi-diagram-2"></span> &nbsp;&nbsp;Split
            </button>
            <button type="button" class="btnOutlined" (click)="refreshData()"><span class="icon ol-refresh"></span>
                &nbsp;Refresh</button>
        </div>
        <div class="inner-container bg-light">

            <div class="d-flex justify-content-between align-items-center px-4 mb-2">
                <div class="d-flex align-items-center">
                    <i class="bi bi-info-square mr-2"></i>
                    <div class="mb-0">Basic Details</div>
                </div>
                <button class="toggle" (click)="toggleForm()">
                    {{ showbasicdetail ? '-' : '+' }}
                </button>
            </div>
        </div>

        <div *ngIf="showbasicdetail" class="details-container mb-4">
            <!-- Group One -->
            <div class="details-group px-4 py-3">
                <div class="details-item">
                    <label for="status" class="labelstyle">OUID</label>
                    <p>{{ data.ouid }}</p>
                </div>
                <div class="details-item">
                    <label for="ouid" class="labelstyle">Status</label>
                    <p>{{ data.status }}</p>
                </div>
                <div class="details-item">
                    <label for="structureType" class="labelstyle">Structure Type</label>
                    <p>{{ data.structureType }}</p>
                </div>
                <div class="details-item">
                    <label for="locationAccuracy" class="labelstyle">Location Accuracy</label>
                    <p>{{ data.locationAccuracy }}</p>
                </div>

            </div>

            <!-- Group Two -->
            <div class="details-group px-4">
                <div class="details-item">
                    <label for="trueLocation" class="labelstyle">Latitude</label>
                    <p>{{ data.trueLocation.latitude }}</p>
                </div>
                <div class="details-item">
                    <label for="trueLocation" class="labelstyle">Longitude</label>
                    <p>{{ data.trueLocation.longitude }}</p>
                </div>
            </div>

        </div>
    </div>

    <div class="ag-grid-container-systemrefrences" *ngIf="showForm">
        <div class="inner-containersys bg-light">

            <div class="d-flex justify-content-between align-items-center px-4 ">
                <div class="d-flex align-items-center">
                    <i class="bi bi-gear mr-2"></i>
                    <div class="mb-0">System References Details</div>
                </div>
                <button class="toggle" (click)="toggle()">
                    {{ showsystemreferences ? '-' : '+' }}
                </button>
            </div>
        </div>

        <div class="table-responsive" *ngIf="showsystemreferences">
            <table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
                <ng-container matColumnDef="select">
                    <th mat-header-cell *matHeaderCellDef></th>
                    <td mat-cell *matCellDef="let element; let i = index">
                        <input type="radio" name="selectedRow" [value]="element" [id]="'row-' + i">
                    </td>
                </ng-container>

                <ng-container matColumnDef="systemType">
                    <th mat-header-cell *matHeaderCellDef mat-sort-header>System Type</th>
                    <td mat-cell *matCellDef="let element"> {{element.systemType}} </td>
                </ng-container>

                <ng-container matColumnDef="structureType">
                    <th mat-header-cell *matHeaderCellDef mat-sort-header>Structure Type </th>
                    <td mat-cell *matCellDef="let element"> {{element.structureType}} </td>
                </ng-container>
                <ng-container matColumnDef="location_accuracy">
                    <th mat-header-cell *matHeaderCellDef mat-sort-header>Location Accuracy</th>
                    <td mat-cell *matCellDef="let element"> {{element.location_accuracy}} </td>
                </ng-container>
                <ng-container matColumnDef="latitude">
                    <th mat-header-cell *matHeaderCellDef mat-sort-header>Latitude</th>
                    <td mat-cell *matCellDef="let element"> {{element.latitude}} </td>
                </ng-container>
                <ng-container matColumnDef="longitude">
                    <th mat-header-cell *matHeaderCellDef mat-sort-header>Longitude</th>
                    <td mat-cell *matCellDef="let element"> {{element.longitude}} </td>
                </ng-container>
                <ng-container matColumnDef="system_ref_id">
                    <th mat-header-cell *matHeaderCellDef mat-sort-header>SystemRefID</th>
                    <td mat-cell *matCellDef="let element"> {{element.system_ref_id}} </td>
                </ng-container>
                <ng-container matColumnDef="other">
                    <th mat-header-cell *matHeaderCellDef mat-sort-header>Other</th>
                    <td mat-cell *matCellDef="let element"> {{element.other}} </td>
                </ng-container>

                <ng-container matColumnDef="toggleDetail">
                    <th mat-header-cell *matHeaderCellDef></th>
                    <td mat-cell *matCellDef="let element">

                        <button mat-icon-button (click)="toggleNestedTable(element)">
                            <mat-icon>{{element.isExpanded ? 'remove' : 'add'}}</mat-icon>
                        </button>

                    </td>
                </ng-container>

                <!-- Main Table Header Row -->
                <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
                <ng-container *ngFor="let element of dataSource.data">

                    <tr mat-row *matRowDef="let element; columns: displayedColumns;"></tr>
                    <!-- Nested Table Row -->
                    <tr class="nested-row" *ngIf="element.isExpanded === true">
                        <td [attr.colspan]="displayedColumns.length">
                            <table mat-table [dataSource]="element.nestedDataSource" class="inner-table">
                                <!-- Nested Table Columns -->
                                <ng-container *ngFor="let key of element.nestedKeys" [matColumnDef]="key">
                                    <th mat-header-cell *matHeaderCellDef> {{ key | titlecase }}</th>
                                    <td mat-cell *matCellDef="let nestedElement"> {{ nestedElement[key] }} </td>
                                </ng-container>

                                <!-- Nested Table Header and Rows -->
                                <tr mat-header-row *matHeaderRowDef="element.nestedKeys"></tr>
                                <tr mat-row *matRowDef="let nestedRow; columns: element.nestedKeys;"></tr>
                            </table>
                        </td>
                    </tr>
                </ng-container>
            </table>
        </div>
    </div>
</div>

This is my typescript file

import { Component, OnInit, OnDestroy } from '@angular/core';
import { OsiApiService } from '../../../../../shared/services/osi-api.service';
import { ViewChild } from '@angular/core';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { AlertHandlerService } from '../../../../../shared/services/alert-handler.service';
import { NgForm } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { debounceTime } from 'rxjs/operators';
import { Subscription } from 'rxjs';
import { ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'app-search-by-ouid',
  templateUrl: './search-by-ouid.component.html',
  styleUrls: ['./search-by-ouid.component.scss']
})
export class SearchbyOUIDComponent implements OnInit, OnDestroy {

  @ViewChild('ouidForm') ouidForm!: NgForm;
  ouid: string = '';
  showForm = false;
  showbasicdetail = true;
  showsystemreferences = true;
  data: any;
  rowDataForSystemReferences: any[] = [];
  dataSource = new MatTableDataSource<any>([]);
  displayedColumns: string[] = ['select', 'systemType', 'structureType', 'latitude', 'longitude', 'location_accuracy', 'toggleDetail',];
  isSearching: boolean = false;
  @ViewChild(MatSort) sort!: MatSort;
  apiresult: boolean = true;

  constructor(private changeDetectorRef: ChangeDetectorRef, private osiApiService: OsiApiService, private alerthandler: AlertHandlerService, private router: Router, private route: ActivatedRoute) { }

  private queryParamsSubscription!: Subscription;
  ngOnInit(): void {
    this.queryParamsSubscription = this.route.queryParams.subscribe(params => {
      if (params['ouid']) {
        if (this.apiresult) {
          this.ouid = params['ouid'];
          this.searchOUID(this.ouid);
        }
      }
    });
  }

  toggleForm() {

    this.showbasicdetail = !this.showbasicdetail;

  }

  toggle() {
    this.showsystemreferences = !this.showsystemreferences;
  }

  searchOUID(ouid: string, event?: Event): void {

    if (event) {
      event.stopPropagation();
    }

    if (this.ouidForm && this.ouidForm.invalid) {
      return;
    }

    if (this.isSearching) return;
    this.isSearching = true;

    this.osiApiService.searchByOUID(ouid).subscribe(response => {
      console.log('API Response:', response);
      //  this.processData(response);

      if (response && response.systemRefs && Array.isArray(response.systemRefs)) {
        this.rowDataForSystemReferences = response.systemRefs.map((ref: any) => {
          const systemRefId = ref.systemRefId || {};
          const location = ref.location || {};


          const nestedKeys = ref.systemRefId ? Object.keys(ref.systemRefId) : [];
          // Create an object with the column names as keys and their corresponding values
          const nestedData = nestedKeys.map(key => ({
            key: key,
            value: ref.systemRefId[key] || '' // Use empty string as a default value if the key doesn't exist
          }));


          const nestedDataSource = new MatTableDataSource(nestedData);

          return {
            systemType: ref.systemType,
            ouid: response.ouid,
            local_id: systemRefId.ServiceId,
            source_system: systemRefId.ServiceDbId,
            latitude: `${location.latitude}`,
            longitude: `${location.longitude}`,
            location_accuracy: response.locationAccuracy,
            system_ref_id: JSON.stringify(systemRefId),
            structureType: ref.structureType,
            isExpanded: false, // This will be used for column definitions in the nested table
            nestedData,
            nestedKeys,
            nestedDataSource,
          };
        });

        this.dataSource = new MatTableDataSource(this.rowDataForSystemReferences);
        this.dataSource.sort = this.sort;
        this.data = response;
        this.showForm = true;
        this.apiresult = false;

        this.router.navigate([], {
          queryParams: { ouid: ouid },
          queryParamsHandling: 'merge',
          replaceUrl: true
        });


        this.isSearching = false;
      }


    }, error => {

      if (error.status === 404) {
        this.ouidModal();
      } else {
        this.errorModal();
      }
      this.isSearching = false;
    });
  }

  toggleNestedTable(element: any): void {
    console.log("Before toggle:", element.isExpanded);
    element.isExpanded = !element.isExpanded;
    console.log("After toggle:", element.isExpanded);
    this.changeDetectorRef.detectChanges();
    console.log("element.nestedKeys:", element.nestedKeys);
    console.log("element.nestedData:", element.nestedData);
    console.log(this.dataSource.data);
  }


  splitData(): void {

    console.log("Split operation to be implemented");
  }

  ouidModal() {
    this.alerthandler.showOuidNotFoundModal();
  }

  errorModal() {
    this.alerthandler.showErrorModal();
  }

  refreshData(): void {

    if (this.ouid) {

      this.searchOUID(this.ouid);


    } else {
      console.log("refreshed successfully");
    }
  }

  ngOnDestroy(): void {
    if (this.queryParamsSubscription) {
      this.queryParamsSubscription.unsubscribe();
    }
  }


}

Data Structure: I'm populating the MatTable with data from an API response. The nested data should be displayed within the table rows when expanded.

Steps Taken:

Checked the API response, and the data seems to be correctly formatted. Added console.log statements to debug the issue. Removed ChangeDetectorRef.detectChanges() to test if it affects the behavior. Expected Outcome: When I click the "Add" button, I expect the nested table to display beneath the corresponding row in the main table.

Actual Outcome: The nested table is not displaying, and I'm not receiving any error messages in the console.

Error Messages: No error messages are displayed in the console.

0

There are 0 best solutions below