19

I have the below data

[
    {
        "_id": "c9d5ab1a",
        "subdomain": "wing",
        "domain": "aircraft",
        "part_id": "c9d5ab1a",
        "info.mimetype": "application/json",
        "info.dependent": "parent",
        "nested": [
            {
                "domain": "aircraft",
                "_id": "c1859902",
                "info.mimetype": "image/jpeg",
                "info.dependent": "c9d5ab1a",
                "part_id": "c1859902",
                "subdomain": "tail"
            }
        ]
    },
    {
        "_id": "1b0b0a26",
        "subdomain": "fuel",
        "domain": "aircraft",
        "part_id": "1b0b0a26",
        "info.mimetype": "image/jpeg",
        "info.dependent": "no_parent"
    }
]

Here if "info.dependent": "parent" then it is nested and if "info.dependent": "no_parent" then it does not have a child. I tried to create a dynamic table but I am stuck on how to make it collapsible/expandable with a nested table. Here is my code on stackblitz.

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

    <ng-container [matColumnDef]="col" *ngFor="let col of displayedColumns">
        <mat-header-cell *matHeaderCellDef> {{ col }} </mat-header-cell>
        <mat-cell *matCellDef="let element"> {{ element[col] }} </mat-cell>
    </ng-container>

    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row;columns:displayedColumns"></mat-row>

</mat-table>

.ts

public data = [
    {
        "_id": "c9d5ab1a",
        "subdomain": "wing",
        "domain": "aircraft",
        "part_id": "c9d5ab1a",
        "info.mimetype": "application/json",
        "info.dependent": "parent",
        "nested": [
            {
                "domain": "aircraft",
                "_id": "c1859902",
                "info.mimetype": "image/jpeg",
                "info.dependent": "c9d5ab1a",
                "part_id": "c1859902",
                "subdomain": "tail"
            }
        ]
    },
    {
        "_id": "1b0b0a26",
        "subdomain": "fuel",
        "domain": "aircraft",
        "part_id": "1b0b0a26",
        "info.mimetype": "image/jpeg",
        "info.dependent": "no_parent"
    }
];

dataSource = new MatTableDataSource([]);
displayedColumns = ['_id', 'subdomain', 'domain', 'part_id', 'info.mimetype', 'info.dependent'];

constructor(){
    this.displayedColumns = this.displayedColumns;
    this.dataSource = new MatTableDataSource(this.data);
}

Required format :--> nested table

The nested format is like below

row 1 --> _id ,subdomain,domain,info.dependent

When we click on that particular row, then it has to expand and display the nested data in a table with the column names and row data.

"nested": [
    {
        "domain": "aircraft",
        "_id": "c1859902",
        "info.mimetype": "image/jpeg",
        "info.dependent": "c9d5ab1a",
        "part_id": "c1859902",
        "subdomain": "tail"
    }
]
2
  • Using the Angular Material Example for a table with expandable rows here where are you getting stuck? Commented Sep 5, 2019 at 17:40
  • In the example it is.mentioned using separate row names.But here i used dynamic row and column names using ngcontainer and after that mat row and in the example there is only day I want to show both col names and row data also Commented Sep 5, 2019 at 17:59

2 Answers 2

38
+50

Note: For those who want to skip the lengthy explanation, here is the StackBlitz example.

What you actually want is to create a nested mat-table where all the nested tables are sortable and can be filtered through as well.

Firstly, since you need to use filtering and sorting in your nested table, you need to create a new MatTableDataSource for it. This can be done initially when you create the main dataSource in the ngOnInit like below.

usersData: User[] = [];

USERS.forEach(user => {
    if (user.addresses && Array.isArray(user.addresses) && user.addresses.length) {
        this.usersData = [...this.usersData, { ...user, addresses: new MatTableDataSource(user.addresses) }];
    } else {
        this.usersData = [...this.usersData, user];
    }
});
this.dataSource = new MatTableDataSource(this.usersData);

From the expandable rows example in the docs, we can see how to create an expandable row. In the expandable row, we will now have a table along with the Filter input. We will add some conditions so that the row is expandable only if there are addresses present.

<div class="example-element-detail" *ngIf="element.addresses?.data.length"
    [@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'">
    <div class="inner-table mat-elevation-z8" *ngIf="expandedElement">
        <mat-form-field>
            <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
        </mat-form-field>
        <table #innerTables mat-table #innerSort="matSort" [dataSource]="element.addresses" matSort>
            <ng-container matColumnDef="{{innerColumn}}" *ngFor="let innerColumn of innerDisplayedColumns">
                <th mat-header-cell *matHeaderCellDef mat-sort-header> {{innerColumn}} </th>
                <td mat-cell *matCellDef="let element"> {{element[innerColumn]}} </td>
            </ng-container>
            <tr mat-header-row *matHeaderRowDef="innerDisplayedColumns"></tr>
            <tr mat-row *matRowDef="let row; columns: innerDisplayedColumns;"></tr>
        </table>
    </div>
</div>

Now that the row expands only if there are nested elements, we need to get rid of the hover for the users which have no addresses

Here is the CSS responsible for adding a background-color on hover

tr.example-element-row:not(.example-expanded-row):hover {
    background: #777;
}

So we just need to add the example-element-row class to our row if the row has an address. If it has no address, the row should not be clickable and there should not be a hover which indicates to the user that the row is in fact not clickable.

<tr mat-row *matRowDef="let element; columns: columnsToDisplay;" 
    [class.example-element-row]="element.addresses?.data.length"
    [class.example-expanded-row]="expandedElement === element"
    (click)="toggleRow(element)">
</tr>

In toggleRow, we will define the logic for what happens when you click a row in the template. We will also implement sort when the user clicks on the row in this function.

@ViewChildren('innerSort') innerSort: QueryList<MatSort>;

toggleRow(element: User) {
    element.addresses && (element.addresses as MatTableDataSource<Address>).data.length ? (this.expandedElement = this.expandedElement === element ? null : element) : null;
    this.cd.detectChanges();
    this.innerTables.forEach((table, index) => (table.dataSource as MatTableDataSource<Address>).sort = this.innerSort.toArray()[index]);
}

Finally, we need to define the applyFilter function so the nested tables can be filtered.

@ViewChildren('innerTables') innerTables: QueryList<MatTable<Address>>;

applyFilter(filterValue: string) {
    this.innerTables.forEach((table, index) => (table.dataSource as MatTableDataSource<Address>).filter = filterValue.trim().toLowerCase());
}

Here is a working example on StackBlitz.

Sign up to request clarification or add additional context in comments.

15 Comments

your example was good but there are 2 thing s to notice 1) The table has to use the dynamic data bcoz we dont know how many columns or row data will come but in the example for nested one you took static data 2) The format which nested requires is like the sample pic which uploaded in question
It was not clear at all from your question that you wanted another table on expansion. I just saw the image you added to your question. Btw, would it really make sense to have a Filter for the inner table? IMHO, a user can't practically have that many address that you'd need a filter there.
please check my updated question here requirement was first display the parent row if has a nested data in it then expand the row with col and row data display it as table
You shouldn't be asking so many questions at once in the same post especially since you didn't mention the filter initally. Read this: meta.stackexchange.com/questions/39223/… and meta.stackoverflow.com/questions/267058/….
@nash11: its replicate, just follow same step in my question. Anyway, I just resolved it. its issue with animation. check my update in stack overflow question. Thanks for replying.
|
1

Looking at the examples from the docs especially the one with the expandable row:

  • You are missing the multiTemplateDataRows directive in the <mat-table>
  • The @detailExpand trigger is missing
  • ...

Here is the example from the docs with your data

Edit (regarding the comment)

Here is how you can get the dynamic columns:

Add this to your component

  getKeys(object): string[] {
    return Object.keys(object);
  }

use the method in the template (template updated according to the attached details screen and the note about the multiple elements under the nested key):

<div class="example-element-descriptions">
    <div *ngFor="let nested of element['nested']"
         class="example-element-description">
        <div *ngIf="element['info.dependent'] === 'parent'">
            <div class="example-element-description__header">
                <div class="example-element-description__cell"
                     *ngFor="let key of getKeys(nested)">{{key}}</div>
            </div>
            <div class="example-element-description__content">
                <div class="example-element-description__cell"
                     *ngFor="let key of getKeys(nested)">{{element[key]}}
                </div>
            </div>
        </div>
        <div *ngIf="element['info.dependent'] === 'no_parent'">no parent</div>
    </div>
</div>

6 Comments

your example was good but there are 2 thing s to notice 1) The table has to use the dynamic data bcoz we dont know how many columns or row data will come but in the example for nested one you took static data 2) The format which nested requires is like the sample pic which uploaded in question
I've uptaded the StackBliitz to use the dynamic columns
there is one doubt stackblitz.com/edit/angular-co1v3t i have updated the json say i have multiple nested objects then bcoz i updated the json and checking with the table but the data it is not displaying
So there can be more objects in the nested list? Ok
Is there any possiblity for showing header for first table bcoz for every it is not imp and how can we get the id of all the rows including id
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.