npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@studiohyperdrive/ngx-table

v18.1.3

Published

An Angular based approach for customizable large scale datatables using Angular CDK.

Downloads

256

Readme

Angular Tools: NgxTable (@studiohyperdrive/ngx-table)

This library provides a quick and easy template based table builder using the Angular CDK Table.

Installation

Install the package first:

npm install @studiohyperdrive/ngx-table

Versioning and build information

This package will follow a semver-like format, major.minor.patch, in which:

  • major: Follows the Angular major version
  • minor: Introduces new features and (potential) breaking changes
  • patch: Introduces bugfixes and minor non-breaking changes

For more information about the build process, authors, contributions and issues, we refer to the ngx-tools repository.

Concept

The Angular CDK Table is a very useful tool to build tables with, but is known to have a rather complex syntax and does not feature a bunch of common use-cases out of the box.

Using the CDK Table the ngx-table provides an easy to use template based system to build tables with, including various common use-cases such as column based sorting, selecting rows, detail rows and more.

Whilst this package is opinionated, the ngx-table works primarily with a ng-template based system, which ensures that any default implementation the table provides can be overwritten.

You can find a StackBlitz example here.

Implementation

Simple example

Starting off with a simple example, the ngx-table works with two primary inputs, the columns and the data inputs.

The columns input expects an array of strings which will represent each column in the table. The keys in this array usually match with properties in the interface of the data you provide to the table, but they do not have to. You can add additional columns that use the entire data object by simply adding keys that are not part of the interface.

The data input provides an array of data that needs to be visualized in the table. Each entry within the array will be represented as a row in the table.

<ngx-table
	[data]="[{name: 'World', firstName: 'Hello', active: true}, {name: 'Tools', firstName: 'NGX', active:false}]"
	[columns]="['name', 'firstName']"
/>

| name | firstName | | ----- | --------- | | World | Hello | | Tools | NGX |

Because of the default templates provided by the table, the above example will render without issues. The table will render the column names as is, so 'name' and 'firstName', and will render the cells as is as well. However, in reality, we want to be able to provide our own templates to each column. We can do this by using the ngx-table-cell.

<ngx-table
	[data]="[{name: 'World', firstName: 'Hello', active: true}, {name: 'Tools', firstName: 'NGX', active:false}]"
	[columns]="['name', 'firstName', 'active']"
>
	<ngx-table-cell column="name">
		<ng-template #headerTmpl> Last name </ng-template>
	</ngx-table-cell>

	<ngx-table-cell column="firstName">
		<ng-template #headerTmpl> First name </ng-template>
	</ngx-table-cell>

	<ngx-table-cell column="active">
		<ng-template #headerTmpl> </ng-template>

		<ng-template #cellTmpl let-active> {{active ? 'Active' | 'Not active'}} </ng-template>
	</ngx-table-cell>
</ngx-table>

| Last name | First name | | | --------- | ---------- | ---------- | | World | Hello | Active | | Tools | NGX | Not active |

Each cell can be connected to a column in the columns array by providing the corresponding key to the column input. From here on out, we can overwrite each part of the column by using the headerTmpl, the cellTmpl or footerTmpl templates. Each template is optional and if not provided will default to the default representation.

Using these templates, we can set our own headers and use the property from the data object to render our cells. But, sometimes we don't need a single property or have columns that don't match with any property in the data objects.

<ngx-table
	[data]="[{name: 'World', firstName: 'Hello', active: true}, {name: 'Tools', firstName: 'NGX', active:false}]"
	[columns]="['active', 'user']"
>
	<ngx-table-cell column="user">
		<ng-template #headerTmpl> User </ng-template>

		<ng-template #cellTmpl let-row="row"> {{row.firstName}} {{row.name}} </ng-template>
	</ngx-table-cell>

	<ngx-table-cell column="active">
		<ng-template #headerTmpl> </ng-template>

		<ng-template #cellTmpl let-active> {{active ? 'Active' | 'Not active'}} </ng-template>
	</ngx-table-cell>
</ngx-table>

| | User | | ---------- | ----------- | | Active | Hello world | | Not active | NGX Tools |

In the cellTmpl template we have several props at our disposal to work with. The implicitly provided prop will try to match the column key with a property in the data object. By grabbing the row property, you can use the entire data object. On top of that the index of the row can be used by using the index property.

The ngx-table package comes with two extra cells that are used in common use-cases, being the ngx-date-table-cell and the ngx-currency-cell. These special cells were made to represent date and currency based data; but are optional. For more information about making your own custom cells, see the topic Custom Cells.

Detail row

ngx-table provides a built-in way to add detail rows to your table. This can be done by providing the #detailRowTmpl template as a ContentChild. When provided, the row automatically becomes clickable and can be toggled open and close. The default props provided to the template are the entire data object and the index. We can listen to a row being clicked by adding a listener to the rowClicked Output.

<ngx-table
	[data]="[{name: 'World', firstName: 'Hello', active: true}, {name: 'Tools', firstName: 'NGX', active:false}]"
	[columns]="['name', 'firstName']"
>
	<ng-template #detailTmpl let-user>
		This user, {{user.firstName}} {{user.name}}, is {{user.active ? 'active' | 'not active'}}.
	</ng-template>
</ngx-table>

Once a detail row is provided, we can use several Inputs to handle the behavior of the table. By setting the showSelectedOpenRow property to true, a class (ngx-table-row-selected) is provided to the open row so we can style the open rows.

We can define how detail rows are opened by using the showDetailRow property. By default, this is set to on-click. This value can also be set to always to set the all rows open, or to on-single-item to make the row open on click and also when there is only one item in the table.

If you wish to emit the rowClicked Output when there's only one item in the table and the showDetailRow is set to on-single-item, you can set the emitValueOnSingleItem to true. The default for this is false, and can be overwritten in the NgxTableConfig.

By passing an index to the defaultRowOpen input, this row will be opened on rendering the table. The standard behavior of the table allows for only one row to be open at all times, which can be overwritten by setting the allowMultipleOpenRows property to true.

We can also visualize the open state of the row by setting the showOpenRowState property to true. This will automatically add a cell at the end of the row displaying the open and closed state of the row with a caret. You can overwrite this template using the openRowStateTmpl

<ngx-table
	[data]="[{name: 'World', firstName: 'Hello', active: true}, {name: 'Tools', firstName: 'NGX', active:false}]"
	[columns]="['name', 'firstName']"
>
	<ng-template #detailTmpl let-user>
		This user, {{user.firstName}} {{user.name}}, is {{user.active ? 'active' | 'not active'}}.
	</ng-template>

	<ng-template #openRowStateTmpl let-open> {{open ? 'Close' : 'Open'}} </ng-template>
</ngx-table>

Loading and empty state

ngx-table also provides a default error, loading and empty state to the table. You can overwrite these templates with the emptyTmpl and loadingTmpl templates.

<ngx-table
	[data]="[{name: 'World', firstName: 'Hello', active: true}, {name: 'Tools', firstName: 'NGX', active:false}]"
	[loading]="true"
	[columns]="['name', 'firstName']"
>
	<ng-template #loadingTmpl> Loading users ... </ng-template>

	<ng-template #emptyTmpl> There are no users in your system! </ng-template>
</ngx-table>

Sorting by column, selecting rows, row-actions and column sorting

Common use-cases such as sorting columns, selecting rows and providing actions on the table are also provided by the ngx-table package.

Sorting can be provided by setting the sortable property to true on a cell. Doing this will automatically add an icon to the column header representing the sorting state of the cell. When providing the sort output, we get an event which contains the sort direction and the key of the column. ngx-table does not sort the data by default, as data provided can be paged.

We can also set the sorting from outside of the table by passing the current sorting to the table using the currentSorting property. This will automatically set the sort state in the table internally.

As always, this default icon can be overwritten on table level by using the sortTmpl template. The implicitly provided prop is the direction of the sorting.

Regardless of the implementation of a sortable column, the custom or default header template will always be wrapped in a button for accessability purposes. This button has been stripped of its styling to provide a more coherent look. Unsortable columns will not be wrapped in a button.

<ngx-table
[data]="[{name: 'World', firstName: 'Hello', active: true}, {name: 'Tools', firstName: 'NGX', active:false}]"
[columns]="['active', 'user']">
    <ngx-table-cell column="user">
        <ng-template #headerTmpl>
            User
        </ng-template>

        <ng-template #cellTmpl let-row="row">
            {{row.firstName}} {{row.name}}
        </ng-template>
    </ngx-table-cell>

    <ngx-table-cell column="active" [sortable]="true" (sort)="sortByActiveState($event)">
        <ng-template #headerTmpl>
        </ng-template>

        <ng-template #cellTmpl let-active>
            {{active ? 'Active' | 'Not active'}}
        </ng-template>
    </ngx-table-cell>

    <ng-template #sortTmpl let-direction>
        <my-custom-sorting-icon [sortDirection]="direction">
    </ng-template>
</ngx-table>

Another built-in common use case provided by the package is selecting rows. The ngx-table component functions as a control value accessor, which means any form control can be attached to the table. By setting the selectable property to true, the table will automatically display a checkbox at the start of every row, including a checkbox at the top of the headers to provide a select-all behavior.

By default, the ngx-table will use the index as the selected value in the form. If we wish to use a property of the data object instead, we can pass the key of the property to the selectableKey Input. The value of this key should always be of type string or number. Just as in previous use-cases, the default template of the checkbox can be overwritten by a custom template using the checkboxTmpl.

<ngx-table
	[selectable]="true"
	[selectableKey]="'id'"
	[data]="[{name: 'World', firstName: 'Hello', active: true, id: '1'}, {name: 'Tools', firstName: 'NGX', active:false, id: '2'}]"
	[loading]="true"
	[columns]="['name', 'firstName']"
>
	<ng-template #checkBoxTmpl let-control>
		<my-custom-checkbox [formControl]="control" />
	</ng-template>
</ngx-table>

On top of being able to select rows, we can also define how we select the rows by passing the selectableType property. By default, the select will be handled by checkboxes, which allows for multiple rows to be selected. This automatically results in a select all option at the top of the table.

When you do not want multiple rows to be selected, you can pass the radio option to the selectableType property. Using this property, the select boxes will become radio buttons and the select all option will no longer be available. If you wish to override the default radio buttons, you can use the radioTmpl template.

<ngx-table
	selectableType="radio"
	selectableKey="id"
	[selectable]="true"
	[data]="[{name: 'World', firstName: 'Hello', active: true, id: '1'}, {name: 'Tools', firstName: 'NGX', active:false, id: '2'}]"
	[loading]="true"
	[columns]="['name', 'firstName']"
>
	<ng-template #radioTmpl let-control>
		<my-custom-radio-button [formControl]="control" />
	</ng-template>
</ngx-table>

Optionally, you can provide an array of actions in the same vain you can add columns to the table. These actions have no special meaning aside from the fact that they will always be rendered behind the regular columns and before the open-row state column. This will provide a consistent look and feel throughout the entire application.

<ngx-table
	[data]="[{name: 'World', firstName: 'Hello', active: true, id: '1'}, {name: 'Tools', firstName: 'NGX', active:false, id: '2'}]"
	[actions]="['delete']"
	[loading]="true"
	[columns]="['name', 'firstName']"
>
	<ngx-table-cell column="delete">
		<ng-template #headerTmpl>
			<button (click)="deleteAll()">Delete all</button>
		</ng-template>

		<ng-template #cellTmpl let-row="row">
			<button (click)="delete(row.id)">Delete user</button>
		</ng-template>
	</ngx-table-cell>
</ngx-table>

As mentioned earlier, the columns input defines the order of the columns by using the order of the provided strings. These columns can be changed on the fly, allowing users to reorder the columns by hand or showing/hiding columns in certain specific situations. A built-in solution to allow drag-and-drop column sorting is not yet provided, but will be in the future.

Header

By default, the header row is always visible, even when no data is provided or when the loading state is active. This behavior can be overwritten in individual tables by hideHeaderWhen, but can also be overwritten globally using the NgxTableConfig.

If you want the header to not be visible when loading or empty or both, you can pass an array of states that define when the headers are not shown. You can pass just when-empty or when-loading, but both can also be applied.

Custom Cells

By using a template-based approach to our cells, we have the ability the create default cells we can use throughout our application. This does not only benefit developers, as it allows for quicker setups of tables, but also provides a consistent look and feel for the users.

ngx-table provides two default specialized columns, the ngx-date-table-cell and the ngx-currency-table-cell which render dates and currencies respectively. Using these cells is entirely optional.

Creating a custom re-usable cell can be done by implementing the NgxAbstractTableCellDirective. Please note that it is required to also include this directive as a provider. This will ensure that the cells can be recognized by the table.

import { Component, ContentChild, TemplateRef, ViewChild } from '@angular/core';

import { NgxAbstractTableCellDirective } from '@studiohyperdrive/ngx-table';

@Component({
	selector: 'ngx-user-table-cell',
	providers: [
		{
			provide: NgxAbstractTableCellDirective,
			useExisting: UserTableCellComponent,
		},
	],
	template: `
		<ng-template #cellTmpl let-user> {{ user.firstName }} {{ user.name }} </ng-template>
	`,
	changeDetection: ChangeDetection.OnPush,
})
export class UserTableCellComponent extends NgxAbstractTableCellDirective {
	/**
	 * A template for the footer of the cell
	 */
	@ContentChild('footerTmpl', { static: false })
	footerTemplate: TemplateRef<any>;

	/**
	 * A template for the header of the cell
	 */
	@ContentChild('headerTmpl', { static: false })
	public headerTemplate: TemplateRef<any>;

	/**
	 * A template for the content of the cell itself
	 */
	@ViewChild('cellTmpl', { static: false })
	public cellTemplate: TemplateRef<any>;
}

In the example above, we create a simple user name cell. Instead of using a ContentChild template for our cell template, we use a ViewChild template. This way, we can provide a default approach to the user name cell, without having to provide it again and again throughout the entire application.

Defaults and NgxTableConfig

The table provides several properties that can be set using an @Input. The majority of these properties are optional and come with a series of default values for easy use. These defaults can be overwritten using the NgxTableConfig.

The NgxTableConfig is an optional InjectionToken that allows for the defaults to be overwritten. These defaults are:

| Defaults | | | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | showDetailRow | Handles the default behavior of detail rows in all tables. By default the detail-rows are shown when clicked. By providing always, the rows will always be open. Providing on-single-item will have the same behavior as the default on-click, but will open the detail row if the table has only one item. | | ngxTableClass | This class will be set on the ngx-table component itself, using HostBinding | | showOpenRowState | Handles the default behavior of the open row state indicator. Providing true will automatically show the indicator in every table with a detail row. | | allowMultipleRowsOpen | Handles the default behavior of whether multiple rows can be open at once. Providing true will allow all tables to always have allow multiple open rows. | | highlightKey | Handles the default behavior of rows that get the ngx-table-row-highlight class. Providing a string here will use that property as the data property to match to add the class. | | showSelectedOpenRow | Handles the default behavior of rows that get the ngx-table-row-selected class. Providing true will always add the class to open rows. | | emitValueOnSingleItem | Handles the default behavior rowClicked emit behavior when the table is set to on-single-item. | | hideHeaderWhen | Handles the default behavior of the header rendering, based on whether we wish to show the header when the table is loading or is empty. |

We can provide the config using a provider. An example of this could be:

providers: [
	{
		provide: NgxTableConfigToken,
		useValue: {
			showDetailRow: 'on-single-item',
			highlightKey: 'is-updated',
		},
	},
];

Styling

By default the ngx-table comes with very minimal styling. Several classes have been provided to help with styling specific parts of the table. As the package was built upon the cdk-table, all styling rules that apply to that table also apply here. Check out the documentation of the cdk-table for more information.

| Class | | | ----------------------------- | --------------------------------------------------------------------------------------------------------------- | | ngx-table | A class set to the table element | | ngx-table-loading | A class set to the table element when the table is displaying a loading template | | ngx-table-clickable-rows | A class set to the table element if a detail row template was provided or the rowClicked output was defined | | ngx-table-hide-rows | A class set to the table element if the data array is empty or when the loading property is set to true | | ngx-table-cell | A class added to all cells in the table | | ngx-table-header-cell | A class added to all header cells in the table | | ngx-table-row-cel | A class added to all row cells in the table | | ngx-table-footer-cell | A class added to all footer cells in the table | | ngx-table-selectable-cell | A class added to all selectable cells in the table | | ngx-table-sorting-cell-button | A class added to the button that is generated around the content of each sortable cell | | ngx-table-sortable-cell | A class added to all sortable header cells in the table | | ngx-table-sorting-icon | A class added to default sorting icon in the table | | ngx-table-detail-cell | A class added to the detail row cell in the table | | ngx-table-row | A class added to rows in the table | | ngx-table-row-odd | A class added to all odd rows in the table | | ngx-table-row-even | A class added to all even rows in the table | | ngx-table-row-selected | A class added to all open rows in the table | | ngx-table-row-highlight | A class added to all rows in the table that match the provided highlightKey property | | ngx-table-detail-row | A class added to all detail rows in the table | | ngx-table-detail-row-open | A class added to all open detail rows in the table | | ngx-table-footer-row | A class added to all footer rows in the table | | ngx-table-open-state-icon | A class added to default open-row state icon in the table | | ngx-table-radio | A class used by the default radio button when the rows are selectable and the selectableType is radio | | ngx-table-checkbox | A class used by the default checkbox when the rows are selectable and the selectableType is checkbox |

If you wish to provide a custom class to the row of your tables, you can provide the class name to the rowClass Input.

Cells can also have a default class we want to provide to all cells of that kind. All ngx-date-table-cell cells have the ngx-date-table-cell class and the same applies for the ngx-currency-table-cell.

Accessibility

ngx-table provides a WCAG and ARIA compliant implementation to tables. When a table has a detail row or is selectable, the role of the table switches from table to treegrid. Because of that, ngx-table follows the WAI ARIA Treegrid Pattern.

Acknowledgements

A big thanks goes out to Sam Verschueren for his help with the initial implementation of this table. Without his help, this table would not have existed.