如何使用角材料在具有可扩展行的表中创建嵌套垫表

Posted

技术标签:

【中文标题】如何使用角材料在具有可扩展行的表中创建嵌套垫表【英文标题】:How to create a nested mat-table in a table with expandable rows using angular material 【发布时间】:2020-01-08 15:25:21 【问题描述】:

我有以下数据

[
    
        "_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"
    
]

这里如果"info.dependent": "parent" 则它是嵌套的,如果"info.dependent": "no_parent" 则它没有子级。我试图创建一个动态表,但我被困在如何使用嵌套表使其可折叠/可扩展。 这是我在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);

要求的格式 :-->

嵌套格式如下

第 1 行 --> _id ,subdomain,domain,info.dependent

当我们单击该特定行时,它必须展开并显示包含列名和行数据的表中的嵌套数据。

"nested": [
    
        "domain": "aircraft",
        "_id": "c1859902",
        "info.mimetype": "image/jpeg",
        "info.dependent": "c9d5ab1a",
        "part_id": "c1859902",
        "subdomain": "tail"
    
]

【问题讨论】:

对具有可扩展行的表格使用 Angular Material 示例here 你在哪里卡住了? 在示例中它是。提到使用单独的行名。但这里我使用 ngcontainer 使用动态行和列名,在该 mat 行之后,在示例中只有一天我想同时显示 col名称和行数据也 【参考方案1】:

注意:对于那些想跳过冗长解释的人,这里是StackBlitz example。

您真正想要的是创建一个嵌套的mat-table,其中所有嵌套的表都是可排序的,也可以进行过滤。

首先,由于您需要在嵌套表中使用过滤和排序,因此您需要为其创建一个新的MatTableDataSource。这最初可以在您在 ngOnInit 中创建主 dataSource 时完成,如下所示。

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);

从可扩展行example in the docs,我们可以看到如何创建可扩展行。在可扩展行中,我们现在将有一个表格以及 Filter 输入。我们将添加一些条件,以便该行仅在存在 addresses 时才可展开。

<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>

现在行只有在嵌套元素时才会展开,我们需要为没有addresses的用户去掉悬停

这是负责在悬停时添加background-color 的 CSS

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

因此,如果行有address,我们只需将example-element-row 类添加到我们的行。如果它没有地址,则该行不应该是可点击的,也不应该有悬停向用户表明该行实际上是不可点击的。

<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>

toggleRow 中,我们将定义单击模板中的一行时发生的逻辑。当用户点击该函数中的行时,我们也会实现sort

@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]);

最后,我们需要定义applyFilter 函数以便过滤嵌套表。

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

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

这是StackBlitz 上的一个工作示例。

【讨论】:

你的例子很好,但有两件事需要注意 1) 表必须使用动态数据,因为我们不知道会有多少列或行数据,但在嵌套的例子中你取静态数据 2) 嵌套需要的格式就像有问题上传的示例图片 从您的问题中根本不清楚您想要另一个扩展表。我刚刚看到您添加到问题中的图像。顺便说一句,为内表设置Filter 真的有意义吗?恕我直言,用户实际上无法拥有那么多您需要过滤器的地址。 请检查我更新的问题,这里的要求是首先显示父行,如果其中有嵌套数据,然后用 col 和行数据展开行并将其显示为表格 你不应该在同一篇文章中一次问这么多问题,尤其是因为你最初没有提到过滤器。请阅读:meta.stackexchange.com/questions/39223/… 和 meta.***.com/questions/267058/…。 @nash11:它的复制品,只需按照我的问题中的相同步骤。无论如何,我刚刚解决了它。它的动画问题。检查我在堆栈溢出问题中的更新。感谢您的回复。【参考方案2】:

看examples from the docs,尤其是the one with the expandable row:

您在&lt;mat-table&gt; 中缺少multiTemplateDataRows 指令 @detailExpand 触发器丢失 ...

Here 是文档中包含您的数据的示例

编辑(关于评论)

以下是获取动态列的方法:

将此添加到您的组件中

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

使用模板中的方法(根据附加的详细信息屏幕和nested键下的多个元素的注释更新模板):

<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>

【讨论】:

你的例子很好,但有两件事需要注意 1) 表必须使用动态数据,因为我们不知道会有多少列或行数据,但在嵌套的例子中你取静态数据 2)嵌套需要的格式就像有问题上传的示例图片 我已经升级 StackBliitz 以使用动态列 有一个疑问 stackblitz.com/edit/angular-co1v3t 我已经更新了 json 说我有多个嵌套对象然后我更新了 json 并检查了表格但它没有显示数据 那么嵌套列表中可以有更多的对象吗?好的 是否有可能为第一个表 bcoz 显示标题,因为它不是 imp 以及我们如何获取所有行的 id,包括 id

以上是关于如何使用角材料在具有可扩展行的表中创建嵌套垫表的主要内容,如果未能解决你的问题,请参考以下文章

将按钮动态添加到垫表角材料

使用角材料的拖放重新排列垫表行

如何在 AngularJS 材料设计中创建简单的搜索输入文本?

如何防止角材料垫菜单关闭?

如何在 WPF 中创建可扩展的 Moebius-strip?

具有垫表初始化的角度材料2分页器