Table

The table component can be either a static or an interactive element. Some tables provide the possiblity to add, remove, edit a row or expand it for further information.

´ Loading interactive demo...
<dt-table [dataSource]="dataSource1"> <ng-container dtColumnDef="host" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>Host</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.host}}</dt-cell> </ng-container> <ng-container dtColumnDef="cpu" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>CPU</dt-header-cell> <dt-cell *dtCellDef="let row; index as i; count as c; first as f; last as l; even as e; odd as o">Row {{i}}/{{c}} {{e ? 'even' : 'odd'}}: {{row.cpu}} {{f ? 'first': ''}} {{l ? 'last' : ''}}</dt-cell> </ng-container> <ng-container dtColumnDef="memory" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef>Memory</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.memory}}</dt-cell> </ng-container> <ng-container dtColumnDef="traffic" dtColumnAlign="control"> <dt-header-cell *dtHeaderCellDef>Network traffic</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.traffic}}</dt-cell> </ng-container> <dt-header-row *dtHeaderRowDef="['host', 'cpu', 'memory', 'traffic']"></dt-header-row> <dt-row *dtRowDef="let row; columns: ['host', 'cpu', 'memory', 'traffic']; index as i; count as c; first as f; last as l; even as e; odd as o" (click)="rowClicked(row, i, c)"></dt-row> </dt-table> export class TableDefaultComponent { dataSource1: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; rowClicked(row: object, index: number, count: number): void { // tslint:disable-next-line LOG.debug(`row ${index}/${count} clicked`, row); } } Host {{row.host}} CPU Row {{i}}/{{c}} {{e ? 'even' : 'odd'}}: {{row.cpu}} {{f ? 'first': ''}} {{l ? 'last' : ''}} Memory {{row.memory}} Network traffic {{row.traffic}} export class TableDefaultComponent { dataSource1: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; rowClicked(row: object, index: number, count: number): void { // tslint:disable-next-line LOG.debug(`row ${index}/${count} clicked`, row); } }

Alignment in tables

Header text should always follow the same alignment as the column content.

Left-aligned content

  • Text
  • Identification numbers beginning with letters. E.g. ID

Center-aligned content

  • Icons
  • Interactive components (e.g. switches)

Right-aligned content

  • Numbers
  • Date, Time, Year,...
  • IP addresses

Tables with two lines per row

Table with two lines
x: 60px

Table entry link

font-family: Bernina Sans Regular
font-size: 14px
color: $turquoise-600

Second row

font-family: Bernina Sans Regular
font-size: 12px
color: $gray-700

Hover

Hover on table row

Hover on table row

border-width: 1px //inside
border-color: $gray-500
Hover on table header

Hover on table header

font-family: Bernina Sans Regular
font-color: $gray-900

Selection

Selection behavior
border-width: 1px //inside
border-color: theme color shade 500

Empty tables

If a table is empty, the information about the reason is shown with the table like in the example here. An illustration can help to visualize the problem.

´ Loading interactive demo...
<button (click)="toggleEmptyState()">Toggle empty state</button> <dt-table [dataSource]="dataSource1"> <ng-container dtColumnDef="usersId" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>Users ID</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.usersId}}</dt-cell> </ng-container> <ng-container dtColumnDef="sessionCount" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef>Session count</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.sessionCount}}</dt-cell> </ng-container> <ng-container dtColumnDef="averageDuration" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef>Average duration</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.averageDuration}}</dt-cell> </ng-container> <ng-container dtColumnDef="errors" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef>Errors</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.errors}}</dt-cell> </ng-container> <ng-container dtColumnDef="country" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>Country</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.country}}</dt-cell> </ng-container> <ng-container dtColumnDef="city" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>City</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.city}}</dt-cell> </ng-container> <ng-container dtColumnDef="browserFamily" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>Browser Family</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.browserFamily}}</dt-cell> </ng-container> <ng-container dtColumnDef="device" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>Device</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.device}}</dt-cell> </ng-container> <dt-table-empty-state dtTableEmptyState> <dt-table-empty-state-image> <img alt="glass" src="data:image/png;base64,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"> </dt-table-empty-state-image> <dt-table-empty-state-title>No data that matches your query</dt-table-empty-state-title> <dt-table-empty-state-message>{{ message }}</dt-table-empty-state-message> </dt-table-empty-state> <dt-header-row *dtHeaderRowDef="['usersId', 'sessionCount', 'averageDuration', 'errors', 'country', 'city', 'browserFamily', 'device']"></dt-header-row> <dt-row *dtRowDef="let row; columns: ['usersId', 'sessionCount', 'averageDuration', 'errors', 'country', 'city', 'browserFamily', 'device'];"></dt-row> </dt-table> export class TableEmptyStateComponent { dataSource: object[] = [ { usersId: 'Alexander@sommers.at', sessionCount: 10, averageDuration: '13.6ms', errors: 6, country: 'Austria', city: 'Linz', browserFamily: 'Chrome', device: 'A1688', }, { usersId: 'maximilian@mustermann.at', sessionCount: 8, averageDuration: '9.99ms', errors: 0, country: 'Austria', city: 'Salzburg', browserFamily: 'Firefox', device: 'A1688', }, { usersId: 'karl@winter.at', sessionCount: 4, averageDuration: '9.55ms', errors: 1, country: 'Austria', city: 'Vienna', browserFamily: 'Firefox', device: 'A1688', }, ]; dataSource1: object[] = []; message = `Amend the timefrime you're querying within or review your query to make your statement less restrictive.`; toggleEmptyState(): void { this.dataSource1 = this.dataSource1.length ? [] : [...this.dataSource]; } } Users ID {{row.usersId}} Session count {{row.sessionCount}} Average duration {{row.averageDuration}} Errors {{row.errors}} Country {{row.country}} City {{row.city}} Browser Family {{row.browserFamily}} Device {{row.device}} glass No data that matches your query {{ message }} export class TableEmptyStateComponent { dataSource: object[] = [ { usersId: 'Alexander@sommers.at', sessionCount: 10, averageDuration: '13.6ms', errors: 6, country: 'Austria', city: 'Linz', browserFamily: 'Chrome', device: 'A1688', }, { usersId: 'maximilian@mustermann.at', sessionCount: 8, averageDuration: '9.99ms', errors: 0, country: 'Austria', city: 'Salzburg', browserFamily: 'Firefox', device: 'A1688', }, { usersId: 'karl@winter.at', sessionCount: 4, averageDuration: '9.55ms', errors: 1, country: 'Austria', city: 'Vienna', browserFamily: 'Firefox', device: 'A1688', }, ]; dataSource1: object[] = []; message = `Amend the timefrime you're querying within or review your query to make your statement less restrictive.`; toggleEmptyState(): void { this.dataSource1 = this.dataSource1.length ? [] : [...this.dataSource]; } }

Problem state indication

Use the red status color for highlighting problematic elements.

Single metric problem indication

If a single metric in a table entry is affected, use the red status color to highlight only the affected value.

Problem indication of a metric

Problem indication of entity or table row

If the problem affects the entity, but the affected metric is not in the table, the entity name is highlighted in red.

Problem indication of a entity

Important note! Don't color hyperlinks even if the whole entity is affected, in this case use the red indicator.

Problem indication of a entity containing a link

Warning indication

Warnings in tables indicate configuration issues.

Warnings in tables
x: 2px

Warning text

font-family: Bernina Sans Regular
font-size: 12px
font-color: $gray-700

Warning icon

Incident icon
color: $warning
icon-width: 16px
icon-height: 16px

Expandable table

To show more details in context of a single table row, use an expandable table.

To guarantee a reasonable perception of the content in the content area, there are some limitations of controls which can be used:

Keep in mind to limit the content in expandable tables for better visual perception. As a rule of thumb, we recommend to keep the content height under 580px (estimated available height on a screen width of 1024px). If there is a lot of content, consider navigating to a details page.

Please keep in mind:

  • Do not put a table or an expandable in an expandable table!
  • Do not add content that requires any kind of pagination!

Expandable tables on entity screens

Expandable table on an entity screen
x: 2px, theme color shade 500
y: 16px
z: 12px
expanded table row: theme color shade 100

Expandable tables on settings pages

Expandable table on settings pages
x: 2px, $gray-700
y: 16px
expanded table row: $gray-200

Edit a table entry

The table component supports two edit modes:

  • Edit within the expandable table, indicated by the dropdown open icon in a table header named 'edit'.
  • Redirect to a separate edit page, indicated by the edit icon, for more complex forms.

Please use the nested button within the table row.

All input fields, as well as update and cancel buttons are then within the expanded table row. The update button gets enabled as soon as changes are made. Triggering either of the buttons, the table row is updated and collapses again.

Edit within expanded table

Delete a table entry

Use remove as a table column header if this action can be reverted (means the value is not in the list any more, but still not deleted). Use delete as a table column header if the value definitely is going to be deleted from the table. The abort icon used in the nested button triggers the remove or delete action.

Delete a table entry
x: 40px
y: 12px
z: 8px

Create a table entry

With the create or add button on top of the table a new table row can be added. The form to create a new table entry replaces the button.

Create a table entry
x: 20px
y: 32px
Form for creating a table entry
x: 16px
y: 20px
z: 60px
border top: 2px line, $gray-200
border bottom:: 2px line, $gray-200
background-color: $gray-100

Example workflow:

Create a table entry

Move up/down

For tables where the sorting of the rows is crucial (e.g. Process group detection rules) the sorter up and sorter down icons are used in a nested button to move rows.

Move up and down of table rows
width: 20px
height: 20px
icon color: $turquoise-600

Filtering in tables

By default, a filter icon and the watermark Filter this table indicate the filter action. The filter is only applied to the table underneath.

Table filter

Icon

width: 16px
height: 16px
color: $turquoise-600

Font

font family: Bernina Sans Regular
font size: 14px
color: $gray-500

Hover of filter button

Table filter hover
icon color: $turquoise-700
font color: $gray-600

Table filter behavior

As soon as filter is focused, an input field appears. The matching text is highlighted in the table entries. Table filter behavior

Table filter styling

Table filter styling
x: 16px
y: 4px
z: 8px

Input field

See input fields page

Behavior

On smaller screens the table converts to an expandable table containing the most right columns as key-value pairs until the width fits the screen. Responsive tables

Combination with other components

Buttons or switches in tables

It is possible to use buttons in tables. Buttons can also appear on hover.

Switches in tables (e.g. to enable/disable monitoring of an entity) are vertically centered in the table row.

Charts in tables

Micro bar charts can be used in tables to visualize information and enable easy comparison. Keep the number of bar charts to one and explain the chart with an appropriate table header. The metric can be added in the same column. For good legibility the metric should be right aligned in front of the bar chart.

Micro bar charts in tables
x: 8px
y: 16px

Colors are always dependent on the environment the chart is in theme colors.

Tables in Overlays

It is possible to use tables in overlays.

Table

´ Loading interactive demo...
<dt-table [dataSource]="dataSource1"> <ng-container dtColumnDef="host" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>Host</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.host}}</dt-cell> </ng-container> <ng-container dtColumnDef="cpu" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>CPU</dt-header-cell> <dt-cell *dtCellDef="let row; index as i; count as c; first as f; last as l; even as e; odd as o">Row {{i}}/{{c}} {{e ? 'even' : 'odd'}}: {{row.cpu}} {{f ? 'first': ''}} {{l ? 'last' : ''}}</dt-cell> </ng-container> <ng-container dtColumnDef="memory" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef>Memory</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.memory}}</dt-cell> </ng-container> <ng-container dtColumnDef="traffic" dtColumnAlign="control"> <dt-header-cell *dtHeaderCellDef>Network traffic</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.traffic}}</dt-cell> </ng-container> <dt-header-row *dtHeaderRowDef="['host', 'cpu', 'memory', 'traffic']"></dt-header-row> <dt-row *dtRowDef="let row; columns: ['host', 'cpu', 'memory', 'traffic']; index as i; count as c; first as f; last as l; even as e; odd as o" (click)="rowClicked(row, i, c)"></dt-row> </dt-table> export class TableDefaultComponent { dataSource1: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; rowClicked(row: object, index: number, count: number): void { // tslint:disable-next-line LOG.debug(`row ${index}/${count} clicked`, row); } } Host {{row.host}} CPU Row {{i}}/{{c}} {{e ? 'even' : 'odd'}}: {{row.cpu}} {{f ? 'first': ''}} {{l ? 'last' : ''}} Memory {{row.memory}} Network traffic {{row.traffic}} export class TableDefaultComponent { dataSource1: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; rowClicked(row: object, index: number, count: number): void { // tslint:disable-next-line LOG.debug(`row ${index}/${count} clicked`, row); } }

Description

The DT Angular data table enhances the Material's cdk table but it also limits it in this first implementation (we are removing unneeded things from the public API) Styleguide: Table Style Guide

Imports

You have to import the DtTableModule when you want to use the dt-table. If you want to use the dt-expandable-cell component, Angular's Angular's BrowserAnimationsModule is also required for animations. For more details on this see Step 2: Animations in the Getting started Guide.

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { DtTableModule } from '@dyntrace/angular-components';

@NgModule({
  imports: [
    BrowserAnimationsModule
    DtTableModule,
  ],
})
class MyModule {}

Component and attribute directives for the table:

Component/Attribute Type Description
dtColumnDef Attribute Name for the column (to be used in the header and row definitions)
dtColumnAlign Attribute Type for the column (to be used in the alignment and for future versions add pipes and masks), possibles types are left-alignment ['left', 'text', 'id'], center-alignment ['center', 'icon', 'control'], right-alignment ['right', 'number', 'date', 'ip']
dtColumnProportion Attribute A number describing the width proportion for the column [dtColumnProportion]=2 means that this column will be double width as the regular ones
dtColumnMinWidth Attribute A CSS string describing the minimum width for the column [dtColumnMinWidth]="'200px'" means that this column will be at least 200px width
dt-header-cell Directive Adds the right classes (the generic dt-header-cell and the cell specific dt-column-css_friendly_column_name) and role (so the browser knows how to parse it. In this case it makes it act as a column header in a native html table)
*dtHeaderCellDef Attribute Captures the template of a column's header cell (the title for the column in the header) and as well as cell-specific properties so that the table can render the header properly.
dt-cell Component Adds the right classes and role (so the browser knows how to parse it. In this case it makes it act as a grid cell in a native html table)
dt-expandable-cell Component Adds the right classes, role and content for the details cell in an expandable table
*dtCellDef Attribute Exports the the row data and the same properties as an *ngFor so that you can define what the cell should show. It also captures the template of the column's data row cell
dt-header-row Component Placeholder for the header row. It is a container that contains the cell outlet. Adds the right class and role
*dtHeaderRowDef Attribute Defines the visible columns in the header out of all the defined ones by receiving a columnName[]
dt-row Component Placeholder for the data rows. It is a container that contains the cell outlet. Adds the right class and role
dt-expandable-row Component Placeholder for the expandable data rows. It is a container that contains the cell outlet and an expandable section. Adds the right class and role
*dtRowDef Attribute Defines the visible columns in each row by receiving a columnName[] and also exposes the same micro-syntax that the dt-cell but for event and property binding
dtTableEmptyState Directive Placeholder for the content displayed when the table is empty
dt-table-empty-state Component Placeholder for the formatted content displayed when the table is empty
dt-table-empty-state-image Component Placeholder for the image or icon to use within the <dt-table-empty-table>
dt-table-empty-state-title Component Placeholder for the title to use within the <dt-table-empty-table>
dt-table-empty-state-message Component Placeholder for the message to use within the <dt-table-empty-table>
dtTableLoadingState Directive Placeholder for the content displayed when the table is loading

Inputs & Outputs

Name Direction Type Default Description
dataSource input object[] | Observable | DataSource undefined Data to be shown in the table
isLoading input boolean false Whether the Table is loading or not

There are no outputs at this stage. The table is totally passive.

Table Usage

The cdk table stablishes a very different approach on how to define the table template. It does not use the native HTML table. So there is no td, tr, th involved. Instead, you need to define all possible columns that the table may show (depending on the data available) and then define the table header and body by selecting from the column definitions, which subset of columns you will show.

Each column definition is created with dt-header-cell and dt-cell inside an ng-container structural directive with a dtColumDef attribute directive applied to it.


<ng-container dtColumnDef="username">
  <dt-header-cell *dtHeaderCellDef> User name </dt-header-cell>
  <dt-cell *dtCellDef="let row"><span ngNonBindable></dt-cell>
</ng-container>

(dtCellDef not only exports the row data but also the same properties as *ngFor using the same micro-syntax) The table header is defined next with a dt-header-row component and a dtHeaderRowDef directive:


<dt-header-row *dtHeaderRowDef="['username', 'age', 'title']"><dt-header-row>

If you want to make the header sticky you can add the dtHeaderRowDefSticky input to the dtHeaderRowDef directive.

<dt-header-row *dtHeaderRowDef="['username', 'age', 'title']; sticky"><dt-header-row>

And finally the table row is defined with a dt-row component and a dtRowDef directive:


<dt-row *dtRowDef="let row; columns: ['username', 'age', 'title']"></dt-row>

(note: The dtRowDef also exports row context, which can be used for event and property bindings on the row element) See the source code of any of the examples in this page to see all the pieces in place

Width proportion

You can customize the column width proportion with [dtColumnProportion]

´ Loading interactive demo...
<dt-table [dataSource]="dataSource1"> <ng-container dtColumnDef="host" dtColumnAlign="text" dtColumnProportion="3"> <dt-header-cell *dtHeaderCellDef>Host</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.host}}</dt-cell> </ng-container> <ng-container dtColumnDef="cpu" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>CPU</dt-header-cell> <dt-cell *dtCellDef="let row;">{{row.cpu}}</dt-cell> </ng-container> <ng-container dtColumnDef="memory" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef>Memory</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.memory}}</dt-cell> </ng-container> <ng-container dtColumnDef="traffic" dtColumnAlign="control"> <dt-header-cell *dtHeaderCellDef>Network traffic</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.traffic}}</dt-cell> </ng-container> <dt-header-row *dtHeaderRowDef="['host', 'cpu', 'memory', 'traffic']"></dt-header-row> <dt-row *dtRowDef="let row; columns: ['host', 'cpu', 'memory', 'traffic'];"></dt-row> </dt-table> export class TableDifferentWidthComponent { dataSource1: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; } Host {{row.host}} CPU {{row.cpu}} Memory {{row.memory}} Network traffic {{row.traffic}} export class TableDifferentWidthComponent { dataSource1: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; }

Minimum Width

You can customize the column minimun width with [dtColumnWidth]

´ Loading interactive demo...
<dt-table [dataSource]="dataSource1"> <ng-container dtColumnDef="host" dtColumnAlign="text" [dtColumnMinWidth]="300"> <dt-header-cell *dtHeaderCellDef>Host</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.host}}</dt-cell> </ng-container> <ng-container dtColumnDef="cpu" dtColumnAlign="text" dtColumnMinWidth="50%"> <dt-header-cell *dtHeaderCellDef>CPU</dt-header-cell> <dt-cell *dtCellDef="let row;">{{row.cpu}}</dt-cell> </ng-container> <ng-container dtColumnDef="memory" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef>Memory</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.memory}}</dt-cell> </ng-container> <ng-container dtColumnDef="traffic" dtColumnAlign="control"> <dt-header-cell *dtHeaderCellDef>Network traffic</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.traffic}}</dt-cell> </ng-container> <dt-header-row *dtHeaderRowDef="['host', 'cpu', 'memory', 'traffic']"></dt-header-row> <dt-row *dtRowDef="let row; columns: ['host', 'cpu', 'memory', 'traffic'];"></dt-row> </dt-table> export class TableMinWidthComponent { dataSource1: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; } Host {{row.host}} CPU {{row.cpu}} Memory {{row.memory}} Network traffic {{row.traffic}} export class TableMinWidthComponent { dataSource1: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; }

Empty state

You can pass an empty state to the table to be displayed when there's no data displayed using the dtTableEmptyState directive The recommended approach to use it is using the following components: <dt-table-empty-state>, <dt-table-empty-state-image>, <dt-table-empty-state-title>, <dt-table-empty-state-message>

´ Loading interactive demo...
<button (click)="toggleEmptyState()">Toggle empty state</button> <dt-table [dataSource]="dataSource1"> <ng-container dtColumnDef="usersId" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>Users ID</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.usersId}}</dt-cell> </ng-container> <ng-container dtColumnDef="sessionCount" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef>Session count</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.sessionCount}}</dt-cell> </ng-container> <ng-container dtColumnDef="averageDuration" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef>Average duration</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.averageDuration}}</dt-cell> </ng-container> <ng-container dtColumnDef="errors" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef>Errors</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.errors}}</dt-cell> </ng-container> <ng-container dtColumnDef="country" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>Country</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.country}}</dt-cell> </ng-container> <ng-container dtColumnDef="city" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>City</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.city}}</dt-cell> </ng-container> <ng-container dtColumnDef="browserFamily" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>Browser Family</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.browserFamily}}</dt-cell> </ng-container> <ng-container dtColumnDef="device" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>Device</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.device}}</dt-cell> </ng-container> <dt-table-empty-state dtTableEmptyState> <dt-table-empty-state-image> <img alt="glass" src="data:image/png;base64,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"> </dt-table-empty-state-image> <dt-table-empty-state-title>No data that matches your query</dt-table-empty-state-title> <dt-table-empty-state-message>{{ message }}</dt-table-empty-state-message> </dt-table-empty-state> <dt-header-row *dtHeaderRowDef="['usersId', 'sessionCount', 'averageDuration', 'errors', 'country', 'city', 'browserFamily', 'device']"></dt-header-row> <dt-row *dtRowDef="let row; columns: ['usersId', 'sessionCount', 'averageDuration', 'errors', 'country', 'city', 'browserFamily', 'device'];"></dt-row> </dt-table> export class TableEmptyStateComponent { dataSource: object[] = [ { usersId: 'Alexander@sommers.at', sessionCount: 10, averageDuration: '13.6ms', errors: 6, country: 'Austria', city: 'Linz', browserFamily: 'Chrome', device: 'A1688', }, { usersId: 'maximilian@mustermann.at', sessionCount: 8, averageDuration: '9.99ms', errors: 0, country: 'Austria', city: 'Salzburg', browserFamily: 'Firefox', device: 'A1688', }, { usersId: 'karl@winter.at', sessionCount: 4, averageDuration: '9.55ms', errors: 1, country: 'Austria', city: 'Vienna', browserFamily: 'Firefox', device: 'A1688', }, ]; dataSource1: object[] = []; message = `Amend the timefrime you're querying within or review your query to make your statement less restrictive.`; toggleEmptyState(): void { this.dataSource1 = this.dataSource1.length ? [] : [...this.dataSource]; } } Users ID {{row.usersId}} Session count {{row.sessionCount}} Average duration {{row.averageDuration}} Errors {{row.errors}} Country {{row.country}} City {{row.city}} Browser Family {{row.browserFamily}} Device {{row.device}} glass No data that matches your query {{ message }} export class TableEmptyStateComponent { dataSource: object[] = [ { usersId: 'Alexander@sommers.at', sessionCount: 10, averageDuration: '13.6ms', errors: 6, country: 'Austria', city: 'Linz', browserFamily: 'Chrome', device: 'A1688', }, { usersId: 'maximilian@mustermann.at', sessionCount: 8, averageDuration: '9.99ms', errors: 0, country: 'Austria', city: 'Salzburg', browserFamily: 'Firefox', device: 'A1688', }, { usersId: 'karl@winter.at', sessionCount: 4, averageDuration: '9.55ms', errors: 1, country: 'Austria', city: 'Vienna', browserFamily: 'Firefox', device: 'A1688', }, ]; dataSource1: object[] = []; message = `Amend the timefrime you're querying within or review your query to make your statement less restrictive.`; toggleEmptyState(): void { this.dataSource1 = this.dataSource1.length ? [] : [...this.dataSource]; } }

Also you can pass custom content using the same dtTableEmptyState

´ Loading interactive demo...
<button (click)="toggleEmptyState()">Toggle empty state</button> <dt-table [dataSource]="dataSource1"> <ng-container dtColumnDef="host" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>Host</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.host}}</dt-cell> </ng-container> <ng-container dtColumnDef="cpu" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>CPU</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.cpu}}</dt-cell> </ng-container> <ng-container dtColumnDef="memory" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef>Memory</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.memory}}</dt-cell> </ng-container> <ng-container dtColumnDef="traffic" dtColumnAlign="control"> <dt-header-cell *dtHeaderCellDef>Network traffic</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.traffic}}</dt-cell> </ng-container> <div dtTableEmptyState style="width: 100%; text-align:center; margin: 3em 0;"> This is the custom content passed. </div> <dt-header-row *dtHeaderRowDef="['host', 'cpu', 'memory', 'traffic']"></dt-header-row> <dt-row *dtRowDef="let row; columns: ['host', 'cpu', 'memory', 'traffic'];"></dt-row> </dt-table> export class TableEmptyCustomStateComponent { dataSource: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; dataSource1: object[] = []; toggleEmptyState(): void { this.dataSource1 = this.dataSource1.length ? [] : [...this.dataSource]; } } Host {{row.host}} CPU {{row.cpu}} Memory {{row.memory}} Network traffic {{row.traffic}}
This is the custom content passed.
export class TableEmptyCustomStateComponent { dataSource: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; dataSource1: object[] = []; toggleEmptyState(): void { this.dataSource1 = this.dataSource1.length ? [] : [...this.dataSource]; } }

Loading state

You can mark the Table as loading using [isLoading] and pass the content to display with dtTableLoadingState directive

´ Loading interactive demo...
<button (click)="toggleLoading()">Toggle loading property</button> <dt-table [dataSource]="dataSource1" [isLoading]="tableLoading"> <ng-container dtColumnDef="host" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>Host</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.host}}</dt-cell> </ng-container> <ng-container dtColumnDef="cpu" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>CPU</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.cpu}}</dt-cell> </ng-container> <ng-container dtColumnDef="memory" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef>Memory</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.memory}}</dt-cell> </ng-container> <ng-container dtColumnDef="traffic" dtColumnAlign="control"> <dt-header-cell *dtHeaderCellDef>Network traffic</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.traffic}}</dt-cell> </ng-container> <dt-loading-distractor dtTableLoadingState>Loading...</dt-loading-distractor> <dt-header-row *dtHeaderRowDef="['host', 'cpu', 'memory', 'traffic']"></dt-header-row> <dt-row *dtRowDef="let row; columns: ['host', 'cpu', 'memory', 'traffic'];"></dt-row> </dt-table> export class TableLoadingComponent { dataSource1: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; tableLoading = true; toggleLoading(): void { this.tableLoading = !this.tableLoading; } } Host {{row.host}} CPU {{row.cpu}} Memory {{row.memory}} Network traffic {{row.traffic}} Loading... export class TableLoadingComponent { dataSource1: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; tableLoading = true; toggleLoading(): void { this.tableLoading = !this.tableLoading; } }

Observable as DataSource

You can pass an Observable to the [dataSource] property

´ Loading interactive demo...
<button (click)="startSubscription()">Start subscription</button> <button (click)="clearRows()">Clear</button> <dt-table [dataSource]="dataSource1"> <ng-container dtColumnDef="host" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>Host</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.host}}</dt-cell> </ng-container> <ng-container dtColumnDef="cpu" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>CPU</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.cpu}}</dt-cell> </ng-container> <ng-container dtColumnDef="memory" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef>Memory</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.memory}}</dt-cell> </ng-container> <ng-container dtColumnDef="traffic" dtColumnAlign="control"> <dt-header-cell *dtHeaderCellDef>Network traffic</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.traffic}}</dt-cell> </ng-container> <dt-header-row *dtHeaderRowDef="['host', 'cpu', 'memory', 'traffic']"></dt-header-row> <dt-row *dtRowDef="let row; columns: ['host', 'cpu', 'memory', 'traffic'];"></dt-row> </dt-table> export class TableObservableComponent { dataSource1 = new BehaviorSubject<object[]>([]); emptyTitle = 'No Host'; emptyMessage = `from 9:00 - 10:00\n Remove filter to make your search less restrictive. Expand or change the timeframe you're searching within.`; // tslint:disable-next-line:no-magic-numbers private source = interval(1000); subscription: Subscription; startSubscription(): void { this.subscription = this.source .pipe(take(MAX_ROWS)) .subscribe((): void => { this.getAnotherRow(); }); } clearRows(): void { this.dataSource1.next([]); } getAnotherRow(): void { // tslint:disable this.dataSource1.next( [...this.dataSource1.value, { host: 'et-demo-2-win4', cpu: `${(Math.random() * 10).toFixed(2)} %`, memory: `${(Math.random() * 10).toFixed(2)} % of ${(Math.random() * 40).toFixed(2)} GB`, traffic: `${(Math.random() * 100).toFixed(2)} Mbit/s`, }]); // tslint:enable } } Host {{row.host}} CPU {{row.cpu}} Memory {{row.memory}} Network traffic {{row.traffic}} export class TableObservableComponent { dataSource1 = new BehaviorSubject([]); emptyTitle = 'No Host'; emptyMessage = `from 9:00 - 10:00\n Remove filter to make your search less restrictive. Expand or change the timeframe you're searching within.`; // tslint:disable-next-line:no-magic-numbers private source = interval(1000); subscription: Subscription; startSubscription(): void { this.subscription = this.source .pipe(take(MAX_ROWS)) .subscribe((): void => { this.getAnotherRow(); }); } clearRows(): void { this.dataSource1.next([]); } getAnotherRow(): void { // tslint:disable this.dataSource1.next( [...this.dataSource1.value, { host: 'et-demo-2-win4', cpu: `${(Math.random() * 10).toFixed(2)} %`, memory: `${(Math.random() * 10).toFixed(2)} % of ${(Math.random() * 40).toFixed(2)} GB`, traffic: `${(Math.random() * 100).toFixed(2)} Mbit/s`, }]); // tslint:enable } }

Dynamic Columns

You can bind the column definitions to an array with a *ngFor directive

´ Loading interactive demo...
<dt-table [dataSource]="dataSource1" [isLoading]="tableLoading"> <ng-container *ngFor="let column of columnsDef;" [dtColumnDef]="column.id" [dtColumnAlign]="column.type"> <dt-header-cell *dtHeaderCellDef>{{ column.title }}</dt-header-cell> <dt-cell *dtCellDef="let row">{{ row[column.id] }}</dt-cell> </ng-container> <dt-header-row *dtHeaderRowDef="columnsName"></dt-header-row> <dt-row *dtRowDef="let row; columns: columnsName;"></dt-row> </dt-table> export class TableDynamicColumnsComponent { columnsDef = [ { id: 'host', title: 'Host', type: 'text', }, { id: 'cpu', title: 'Cpu', type: 'text', }, { id: 'memory', title: 'Memory', type: 'number', }, { id: 'traffic', title: 'Traffic', type: 'control', }, ]; columnsName = this.columnsDef.map((col) => (col.id)); dataSource1: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; tableLoading = true; toggleLoading(): void { this.tableLoading = !this.tableLoading; } } {{ column.title }} {{ row[column.id] }} export class TableDynamicColumnsComponent { columnsDef = [ { id: 'host', title: 'Host', type: 'text', }, { id: 'cpu', title: 'Cpu', type: 'text', }, { id: 'memory', title: 'Memory', type: 'number', }, { id: 'traffic', title: 'Traffic', type: 'control', }, ]; columnsName = this.columnsDef.map((col) => (col.id)); dataSource1: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; tableLoading = true; toggleLoading(): void { this.tableLoading = !this.tableLoading; } }

The DataSource type is an abstract class with two methods: connect and disconnect. Connect has to return an Observable that the table subscribes to. Disconnect does cleanup. Usin this class to wrap the data provided for the table allows for maximum flexibility and will be the responsible of a future sort, and filter functionalities

Expandable Table Rows

Expandable rows can be defined using dt-expandable-row. An optional details cell can be added using dt-expandable-cell.

´ Loading interactive demo...
<dt-table [dataSource]="dataSource1"> <ng-container dtColumnDef="host" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>Host</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.host}}</dt-cell> </ng-container> <ng-container dtColumnDef="cpu" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>CPU</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.cpu}}</dt-cell> </ng-container> <ng-container dtColumnDef="memory" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef>Memory</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.memory}}</dt-cell> </ng-container> <ng-container dtColumnDef="traffic" dtColumnAlign="control"> <dt-header-cell *dtHeaderCellDef>Network traffic</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.traffic}}</dt-cell> </ng-container> <ng-container dtColumnDef="details" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef>Details</dt-header-cell> <dt-expandable-cell *dtCellDef></dt-expandable-cell> </ng-container> <dt-header-row *dtHeaderRowDef="['host', 'cpu', 'memory', 'traffic', 'details']"></dt-header-row> <dt-expandable-row *dtRowDef="let row; columns: ['host', 'cpu', 'memory', 'traffic', 'details']; let rowIndex=index"> Expandable section for {{row.name}} </dt-expandable-row> </dt-table> export class TableExpandableRowsComponent { dataSource1: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; } Host {{row.host}} CPU {{row.cpu}} Memory {{row.memory}} Network traffic {{row.traffic}} Details Expandable section for {{row.name}} export class TableExpandableRowsComponent { dataSource1: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; }

Options & Properties of DtExpandableRow

Name Type Default Description
@Ouput() openedChange EventEmitter<DtExpandableRow> Event emitted when the expanded state changes.
@Input() multiple boolean false Sets the mode for expanding multiple rows at a time. NOTE: must not be used in Dynatrace UI!
expanded boolean false Gets or sets the expanded state of a row.
contentViewContainer ViewContainerRef Gets a reference to the expandable container for dynamically adding components.

Programmatic access

Through the table object

  • addColumnDef()
  • removeColumnDef()
  • addRowDef()
  • removeRowDef()
  • renderRows()
  • setHeaderRowDef()

Through the header object

  • getColumnsDiff()

Styling

The styling will be set for the core component and cannot be changed by the developer using the component. The component should be them-able, though. The only things the developer can set are

  • the type of table which changes not only the functionality available but also (behind the scenes) some of the styling.
  • the responsiveness of the table

type TableType = 'static' | 'dynamic'
responsive: Boolean

Ex:
...

Both type and responsive are optional and have default values

  • type -> static
  • responsive -> true

The table will always

  • show a zebra pattern between alternating rows.
  • align each data type (and its column header) in a specific way

Theming

The table styling depends on the theme the component is in. You can set a theme on an area of the app by using the dtTheme directive.

To apply a sticky header to the table set the sticky input on the dtHeaderRowDef directive.

´ Loading interactive demo...
<dt-table [dataSource]="dataSource1"> <ng-container dtColumnDef="host" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>Host</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.host}}</dt-cell> </ng-container> <ng-container dtColumnDef="cpu" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>CPU</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.cpu}}</dt-cell> </ng-container> <ng-container dtColumnDef="memory" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef>Memory</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.memory}}</dt-cell> </ng-container> <ng-container dtColumnDef="traffic" dtColumnAlign="control"> <dt-header-cell *dtHeaderCellDef>Network traffic</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.traffic}}</dt-cell> </ng-container> <dt-header-row *dtHeaderRowDef="['host', 'cpu', 'memory', 'traffic']; sticky: true"></dt-header-row> <dt-row *dtRowDef="let row; columns: ['host', 'cpu', 'memory', 'traffic']"></dt-row> </dt-table> export class TableStickyHeaderComponent { dataSource1: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; } Host {{row.host}} CPU {{row.cpu}} Memory {{row.memory}} Network traffic {{row.traffic}} export class TableStickyHeaderComponent { dataSource1: object[] = [ { host: 'et-demo-2-win4', cpu: '30 %', memory: '38 % of 5.83 GB', traffic: '98.7 Mbit/s' }, { host: 'et-demo-2-win3', cpu: '26 %', memory: '46 % of 6 GB', traffic: '625 Mbit/s' }, { host: 'docker-host2', cpu: '25.4 %', memory: '38 % of 5.83 GB', traffic: '419 Mbit/s' }, { host: 'et-demo-2-win1', cpu: '23 %', memory: '7.86 % of 5.83 GB', traffic: '98.7 Mbit/s' }, ]; }

Sorting

´ Loading interactive demo...
<dt-table [dataSource]="dataSource" dtSort (dtSortChange)="sortData($event)"> <ng-container dtColumnDef="host" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef dt-sort-header sort-aria-label="Change sort order for hosts">Host</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.host}}</dt-cell> </ng-container> <ng-container dtColumnDef="cpu" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef dt-sort-header sort-aria-label="Change sort order for cpus">CPU</dt-header-cell> <dt-cell *dtCellDef="let row; index as i; count as c; first as f; last as l; even as e; odd as o">{{row.cpu | dtPercent}}</dt-cell> </ng-container> <ng-container dtColumnDef="memory" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef dt-sort-header start="desc" sort-aria-label="Change sort order for memory">Memory</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.memoryPerc | dtPercent}} of {{row.memoryTotal | dtBytes }}</dt-cell> </ng-container> <ng-container dtColumnDef="traffic" dtColumnAlign="control"> <dt-header-cell *dtHeaderCellDef dt-sort-header sort-aria-label="Change sort order for network traffic">Network traffic</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.traffic | dtBytes | dtRate: 's' }}</dt-cell> </ng-container> <dt-header-row *dtHeaderRowDef="['host', 'cpu', 'memory', 'traffic']"></dt-header-row> <dt-row *dtRowDef="let row; columns: ['host', 'cpu', 'memory', 'traffic']; index as i; count as c; first as f; last as l; even as e; odd as o"></dt-row> </dt-table> export class TableSortingComponent { dataSource: Array<{ host: string; cpu: number; memoryPerc: number; memoryTotal: number; traffic: number }> = [ { host: 'et-demo-2-win3', cpu: 26, memoryPerc: 46, memoryTotal: 6000000000, traffic: 62500000 }, { host: 'et-demo-2-win4', cpu: 30, memoryPerc: 38, memoryTotal: 5830000000, traffic: 98700000 }, { host: 'docker-host2', cpu: 25.4, memoryPerc: 35, memoryTotal: 5810000000, traffic: 41900000 }, { host: 'et-demo-2-win1', cpu: 23, memoryPerc: 7.86, memoryTotal: 5820000000, traffic: 98700000 }, ]; sortData(event: DtSortEvent): void { const data = this.dataSource.slice(); this.dataSource = data.sort((a, b) => { const isAsc = event.direction === 'asc'; switch (event.active) { case 'host': return this.compare(a.host, b.host, isAsc); case 'cpu': return this.compare(a.cpu, b.cpu, isAsc); case 'memory': return this.compare(a.memoryPerc, b.memoryPerc, isAsc); case 'traffic': return this.compare(a.traffic, b.traffic, isAsc); default: return 0; } }); } compare(a: number | string, b: number | string, isAsc: boolean): number { return (a < b ? -1 : 1) * (isAsc ? 1 : -1); } } Host {{row.host}} CPU {{row.cpu | dtPercent}} Memory {{row.memoryPerc | dtPercent}} of {{row.memoryTotal | dtBytes }} Network traffic {{row.traffic | dtBytes | dtRate: 's' }} export class TableSortingComponent { dataSource: Array<{ host:="" string;="" cpu:="" number;="" memoryperc:="" memorytotal:="" traffic:="" number="" }=""> = [ { host: 'et-demo-2-win3', cpu: 26, memoryPerc: 46, memoryTotal: 6000000000, traffic: 62500000 }, { host: 'et-demo-2-win4', cpu: 30, memoryPerc: 38, memoryTotal: 5830000000, traffic: 98700000 }, { host: 'docker-host2', cpu: 25.4, memoryPerc: 35, memoryTotal: 5810000000, traffic: 41900000 }, { host: 'et-demo-2-win1', cpu: 23, memoryPerc: 7.86, memoryTotal: 5820000000, traffic: 98700000 }, ]; sortData(event: DtSortEvent): void { const data = this.dataSource.slice(); this.dataSource = data.sort((a, b) => { const isAsc = event.direction === 'asc'; switch (event.active) { case 'host': return this.compare(a.host, b.host, isAsc); case 'cpu': return this.compare(a.cpu, b.cpu, isAsc); case 'memory': return this.compare(a.memoryPerc, b.memoryPerc, isAsc); case 'traffic': return this.compare(a.traffic, b.traffic, isAsc); default: return 0; } }); } compare(a: number | string, b: number | string, isAsc: boolean): number { return (a < b ? -1 : 1) * (isAsc ? 1 : -1); } }

The DtSort and dt-sort-header are used to add sorting functionality to the table.

To add sorting capabilities to your tables add the dtSort directive to the dt-table component. For each column that should be sortable by the user add dt-sort-header to the dt-header-cell. The dt-sort-header registers itself with the id given to the dtColumnDef with the DtSort directive.

<dt-table ... dtSort ...>

And use the dt-sort-header component for the header cells.

<dt-header-cell dt-sort-header ...>

DtSort

You can set the following inputs and outputs on the dtSort directive.

Name Type Default Description
@Input() dtSortActive string The id of the most recent active column
@Input() dtSortDirection DtSortDirection asc The sort direction of the currently active column
@Input() dtSortDisabled boolean false Wether sorting is disabled for the entire table
@Input() dtSortStart DtSortDirection Whe direction to set when an DtSortHeader is initially sorted. May be overriden by the DtSortHeader's sort start.
@Output('dtSortChange') sortChange EventEmitter<DtSortEvent> Event emmited when the user changes either the active sort or the sorting direction.

Methods

| Name | Description | Parameters | Return value | | --- | --- | --- | | sort | Sets the active sort id and new sort direction | sortable: DtSortHeader | void |

DtSortHeader

You can set the following inputs and outputs on the dt-sort-header component.

Name Type Default Description
@Input() disabled boolean Wether sorting is disabled for this sort header
@Input() start DtSortDirection asc Overrides the sort start value of the containing DtSort.
@Input() sort-aria-label string Sets the aria label for the button used for sorting

Accessibility

Please provide a sort-aria-label for each dt-sort-header to make the sorting experience accessible for all users. E.g. Change sort order for column hosts.

DtSortDirection

The type used for the sort direction either asc or desc

DtSortEvent

The event emitted when the user changes either the active sort or the sorting direction. The event contains the following properties.

Name Type Description
active string the id of the currently active column
direction DtSortDirection The direction for the currently active column

To see a combination with initial sort direction and active column and disabling behaviour - please see the following complex example.

´ Loading interactive demo...
<button dt-button (click)="toggleDisableAll()">Toggle disable sorting for all columns</button> <dt-table [dataSource]="dataSource" dtSort (dtSortChange)="sortData($event)" [dtSortDisabled]="disableAll" [dtSortActive]="'cpu'" dtSortStart="asc" dtSortDirection="desc"> <ng-container dtColumnDef="host" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef dt-sort-header sort-aria-label="Change sort order for hosts">Host</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.host}}</dt-cell> </ng-container> <ng-container dtColumnDef="cpu" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef dt-sort-header sort-aria-label="Change sort order for cpus">CPU</dt-header-cell> <dt-cell *dtCellDef="let row; index as i; count as c; first as f; last as l; even as e; odd as o">{{row.cpu | dtPercent}}</dt-cell> </ng-container> <ng-container dtColumnDef="memory" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef dt-sort-header start="desc" sort-aria-label="Change sort order for memory">Memory</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.memoryPerc | dtPercent}} of {{row.memoryTotal | dtBytes }}</dt-cell> </ng-container> <ng-container dtColumnDef="traffic" dtColumnAlign="control"> <dt-header-cell *dtHeaderCellDef disabled dt-sort-header sort-aria-label="Change sort order for network traffic">Network traffic</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.traffic | dtBytes | dtRate: 's' }}</dt-cell> </ng-container> <dt-header-row *dtHeaderRowDef="['host', 'cpu', 'memory', 'traffic']"></dt-header-row> <dt-row *dtRowDef="let row; columns: ['host', 'cpu', 'memory', 'traffic']; index as i; count as c; first as f; last as l; even as e; odd as o"></dt-row> </dt-table> export class TableSortingFullComponent { disableAll = false; dataSource: Array<{ host: string; cpu: number; memoryPerc: number; memoryTotal: number; traffic: number }> = [ { host: 'et-demo-2-win4', cpu: 30, memoryPerc: 38, memoryTotal: 5830000000, traffic: 98700000 }, { host: 'et-demo-2-win3', cpu: 26, memoryPerc: 46, memoryTotal: 6000000000, traffic: 62500000 }, { host: 'docker-host2', cpu: 25.4, memoryPerc: 35, memoryTotal: 5810000000, traffic: 41900000 }, { host: 'et-demo-2-win1', cpu: 23, memoryPerc: 7.86, memoryTotal: 5820000000, traffic: 98700000 }, ]; sortData(event: DtSortEvent): void { const data = this.dataSource.slice(); this.dataSource = data.sort((a, b) => { const isAsc = event.direction === 'asc'; switch (event.active) { case 'host': return this.compare(a.host, b.host, isAsc); case 'cpu': return this.compare(a.cpu, b.cpu, isAsc); case 'memory': return this.compare(a.memoryPerc, b.memoryPerc, isAsc); case 'traffic': return this.compare(a.traffic, b.traffic, isAsc); default: return 0; } }); } compare(a: number | string, b: number | string, isAsc: boolean): number { return (a < b ? -1 : 1) * (isAsc ? 1 : -1); } toggleDisableAll(): void { this.disableAll = !this.disableAll; } } Host {{row.host}} CPU {{row.cpu | dtPercent}} Memory {{row.memoryPerc | dtPercent}} of {{row.memoryTotal | dtBytes }} Network traffic {{row.traffic | dtBytes | dtRate: 's' }} export class TableSortingFullComponent { disableAll = false; dataSource: Array<{ host:="" string;="" cpu:="" number;="" memoryperc:="" memorytotal:="" traffic:="" number="" }=""> = [ { host: 'et-demo-2-win4', cpu: 30, memoryPerc: 38, memoryTotal: 5830000000, traffic: 98700000 }, { host: 'et-demo-2-win3', cpu: 26, memoryPerc: 46, memoryTotal: 6000000000, traffic: 62500000 }, { host: 'docker-host2', cpu: 25.4, memoryPerc: 35, memoryTotal: 5810000000, traffic: 41900000 }, { host: 'et-demo-2-win1', cpu: 23, memoryPerc: 7.86, memoryTotal: 5820000000, traffic: 98700000 }, ]; sortData(event: DtSortEvent): void { const data = this.dataSource.slice(); this.dataSource = data.sort((a, b) => { const isAsc = event.direction === 'asc'; switch (event.active) { case 'host': return this.compare(a.host, b.host, isAsc); case 'cpu': return this.compare(a.cpu, b.cpu, isAsc); case 'memory': return this.compare(a.memoryPerc, b.memoryPerc, isAsc); case 'traffic': return this.compare(a.traffic, b.traffic, isAsc); default: return 0; } }); } compare(a: number | string, b: number | string, isAsc: boolean): number { return (a < b ? -1 : 1) * (isAsc ? 1 : -1); } toggleDisableAll(): void { this.disableAll = !this.disableAll; } }

Problem & Warning Indicator

To get an error or warning indicator use the dtIndicator directive inside your dt-cell components on any html element or on your dt-cell component directly. The dt-row will pick up if any dtIndicator was used inside the row's dt-cell and show the correct indicator. If one indicator has color error set the indicator on the row is an error indicator. Error trumps warning. You can control the active state of the indicator by using the input named the same as dtIndicator.

<dt-cell [dtIndicator]="active" ...>

The full example below shows both usages - single metric inside a cell and the entire cell enhanced with the dtIndicator

´ Loading interactive demo...
<dt-table [dataSource]="_dataSource"> <ng-container dtColumnDef="host" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>Host</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.name}}</dt-cell> </ng-container> <ng-container dtColumnDef="cpu" dtColumnAlign="text"> <dt-header-cell *dtHeaderCellDef>CPU</dt-header-cell> <dt-cell [dtIndicator]="metricHasProblem(row, 'cpuUsage')" [dtIndicatorColor]="metricIndicatorColor(row, 'cpuUsage')" *dtCellDef="let row"> {{row.cpuUsage | dtPercent }} </dt-cell> </ng-container> <ng-container dtColumnDef="memory" dtColumnAlign="number"> <dt-header-cell *dtHeaderCellDef>Memory</dt-header-cell> <dt-cell *dtCellDef="let row"> <span [dtIndicator]="metricHasProblem(row, 'memoryPerc')" [dtIndicatorColor]="metricIndicatorColor(row, 'memoryPerc')">{{row.memoryPerc | dtPercent }}</span>&nbsp;of&nbsp; <span [dtIndicator]="metricHasProblem(row, 'memoryTotal')" [dtIndicatorColor]="metricIndicatorColor(row, 'memoryTotal')">{{row.memoryTotal | dtBytes}}</span> </dt-cell> </ng-container> <ng-container dtColumnDef="traffic" dtColumnAlign="control"> <dt-header-cell *dtHeaderCellDef>Network traffic</dt-header-cell> <dt-cell *dtCellDef="let row">{{row.traffic | dtBits | dtRate: 's' }}</dt-cell> </ng-container> <ng-container dtColumnDef="empty"> <dt-cell *dtCellDef="let row" >This is empty</dt-cell> </ng-container> <dt-header-row *dtHeaderRowDef="['host', 'cpu', 'memory', 'traffic']"></dt-header-row> <dt-row *dtRowDef="let row; columns: ['host', 'cpu', 'memory', 'traffic']"></dt-row> </dt-table> <button dt-button (click)="_toggleProblem()">Toggle problem</button> export class TableProblemComponent { _dataSource: TableData[] = [ { name: 'et-demo-2-win4', cpuUsage: 30, memoryPerc: 38, memoryTotal: 5830000000, traffic: 987000000, warnings: ['memoryPerc'], errors: ['cpuUsage'] }, { name: 'et-demo-2-win3', cpuUsage: 26, memoryPerc: 46, memoryTotal: 6000000000, traffic: 6250000000 }, { name: 'docker-host2', cpuUsage: 25.4, memoryPerc: 38, memoryTotal: 5250000000, traffic: 4190000000, warnings: ['cpuUsage'] }, { name: 'et-demo-2-win1', cpuUsage: 23, memoryPerc: 7.86, memoryTotal: 16000000000, traffic: 987000000 }, ]; metricHasProblem(rowData: TableData, metricName: string): boolean { return this._metricHasError(rowData, metricName) || this._metricHasWarning(rowData, metricName); } metricIndicatorColor(rowData: TableData, metricName: string): 'error' | 'warning' | null { return this._metricHasError(rowData, metricName) ? 'error' : this._metricHasWarning(rowData, metricName) ? 'warning' : null; } private _metricHasError(rowData: TableData, metricName: string): boolean { return rowData.errors !== undefined && rowData.errors.includes(metricName); } private _metricHasWarning(rowData: TableData, metricName: string): boolean { return rowData.warnings !== undefined && rowData.warnings.includes(metricName); } _toggleProblem(): void { if (this._dataSource[0].errors) { delete this._dataSource[0].errors; } else { this._dataSource[0].errors = ['cpuUsage']; } } } Host {{row.name}} CPU {{row.cpuUsage | dtPercent }} Memory {{row.memoryPerc | dtPercent }} of  {{row.memoryTotal | dtBytes}} Network traffic {{row.traffic | dtBits | dtRate: 's' }} This is empty export class TableProblemComponent { _dataSource: TableData[] = [ { name: 'et-demo-2-win4', cpuUsage: 30, memoryPerc: 38, memoryTotal: 5830000000, traffic: 987000000, warnings: ['memoryPerc'], errors: ['cpuUsage'] }, { name: 'et-demo-2-win3', cpuUsage: 26, memoryPerc: 46, memoryTotal: 6000000000, traffic: 6250000000 }, { name: 'docker-host2', cpuUsage: 25.4, memoryPerc: 38, memoryTotal: 5250000000, traffic: 4190000000, warnings: ['cpuUsage'] }, { name: 'et-demo-2-win1', cpuUsage: 23, memoryPerc: 7.86, memoryTotal: 16000000000, traffic: 987000000 }, ]; metricHasProblem(rowData: TableData, metricName: string): boolean { return this._metricHasError(rowData, metricName) || this._metricHasWarning(rowData, metricName); } metricIndicatorColor(rowData: TableData, metricName: string): 'error' | 'warning' | null { return this._metricHasError(rowData, metricName) ? 'error' : this._metricHasWarning(rowData, metricName) ? 'warning' : null; } private _metricHasError(rowData: TableData, metricName: string): boolean { return rowData.errors !== undefined && rowData.errors.includes(metricName); } private _metricHasWarning(rowData: TableData, metricName: string): boolean { return rowData.warnings !== undefined && rowData.warnings.includes(metricName); } _toggleProblem(): void { if (this._dataSource[0].errors) { delete this._dataSource[0].errors; } else { this._dataSource[0].errors = ['cpuUsage']; } } }

NOTE:

Right now only setting the light or dark mode is available. Full theming functionality will be added in a later stage. As per the style guide: "The table component can be either a static or an interactive element. It can be used to drill down to a details page and there is also the possibility to add, remove, edit a table row or even expand a row for further information."

This version of the core table component just addresses a static data table. No user interaction with it allowed. So just groundhog's standard and responsive tables (equivalent to classes .table .table–responsive). This version does not yet allow for any of the following capabilities that Dynatrace's data tables need

  • two lines per row tables.
  • on hover reaction of any type (ex: on hover tool tips for icons)
  • Filtering
  • Pagination
  • Problem/Warning indicators
  • Edit mode
  • Add/Remove/Delete rows
  • Move Up/Down
  • Buttons or Charts as content

All pending functionality will be progressively addressed by future versions