Examples

Getting Started

Installation

$ npm install --save @transunion-ui/tablejs

Implementation

Set Up

1. Import the TablejsModule into your app.module.ts file.

import { TablejsModule } from '@transunion-ui/tablejs';

2. Add the TablejsModule to the list of imports in your app.module.ts file.

@NgModule ({
	declarations: [ ... ],
	imports: [ TablejsModule ],
	providers: [ ... ]
});

3. Let’s create a component and place some HTML table markup in the component’s Typescript file. Consider the following HTML:

<table>
  <thead>
    <tr>
      <th>
        <div>Name</div>
      </th>
      <th>
        <div>Purchase</div>
      </th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        <div class="name">The Purchaser's Name</div>
      </td>
      <td>
        <div class="item">The item purchased</div>
      </td>
    </tr>
  </tbody>
</table>

4. And its corresponding SCSS:

table {
  font-family: Lato;
  table-layout: fixed;
  border-collapse: collapse;
  border-spacing: 0px;
  padding: 0;
  margin: 0;
  width: 100%;
  thead {
    display: table-header-group;
  }
  tbody {
    display: table-row-group;
  }
  thead,
  tbody {
    position: relative;
    left: 0px;
    top: 0px;
    width: 100%;

    tr {
      display: grid;
      grid-template-columns: 30% 70%;

      th,
      td {
        position: relative;
        border: 1px solid #e9e9e9;
        height: 36px;
        width: 100%;
        div {
          position: relative;
          padding: 8px;
          width: 100%;
          height: 100%;
          overflow: hidden;
          white-space: nowrap;
          text-overflow: ellipsis;
        }
        th {
          text-align: center;
          background-color: #555555;
          div {
            color: #ffffff;
            font-size: 15px;
          }
        }
        td {
          background-color: #ffffff;
          div {
            font-size: 13px;
            &.name {
              width: 100%;
            }
            &.item {
              width: 100%;
            }
          }
        }
      }
    }
  }
}

You should now have a table that looks something like this:

Getting Started Table

5. Now that we have a simple table in place, let’s display some dynamic data in the table. Create an array of items in your component’s Typescript file for displaying in the table.

purchases: any[] = [
  {
    name: 'John Hughes',
    item: 'Cameras',
  },
  {
    name: 'Mickey Mouse',
    item: 'Whistles',
  },
  {
    name: 'Amelia Earhart',
    item: 'Goggles',
  },
  {
    name: 'Godzilla',
    item: 'Breath Mints',
  },
];

To display this data in the template, we will use the ngFor directive to iterate over each item in the array. Then replace our static name and purchase item with the corresponding variables from our array.

  <table>
    <thead>
      <tr>
        <th>
          <div>Name</div>
        </th>
        <th>
          <div>Purchase</div>
        </th>
      </tr>
    </thead>

    <tbody>
      <tr *ngFor="let purchase of purchases">
        <td>
          <div class="name">{{ purchase.name }}</div>
        </td>
        <td>
          <div class="item">{{ purchase.item }}</div>
        </td>
      </tr>
    </tbody>
  </table>

TableJS Initialization

If we want to enable resize or reordering, we will need to supply TableJS with row, column, and viewport information.

1. Register rows: TableJS will be able to track row information once we place the tablejsGridRow directive on each tr element.

  <tablejs-grid>
    <table>
      <thead>
        <tr tablejsGridRow>
          <th>
            <div>Name</div>
          </th>
          <th>
            <div>Purchase</div>
          </th>
        </tr>
      </thead>

      <tbody>
        <tr *ngFor="let purchase of purchases" tablejsGridRow>
          <td>
            <div class="name">{{ purchase.name }}</div>
          </td>
          <td>
            <div class="item">{{ purchase.item }}</div>
          </td>
        </tr>
      </tbody>
    </table>
  </tablejs-grid>

Great! TableJS can now register your rows!

2. Declare column data: Next, we will need to tie the columns from the thead tag to the columns in the tbody tag for sizing calculations. TableJS uses CSS classes to accomplish this. Let’s replace the class attribute on the td div element with the tablejsDataColClassdirective. The class name supplied to the tablejsDataColClass directive will automatically be placed on the td div when the page renders.

<tablejs-grid>
  <table>
    <thead>
      <tr tablejsGridRow>
        <th>
          <div>Name</div>
        </th>
        <th>
          <div>Purchase</div>
        </th>
      </tr>
    </thead>

    <tbody>
      <tr *ngFor="let purchase of purchases" tablejsGridRow>
        <td>
          <div tablejsDataColClass="name">{{ purchase.name }}</div>
        </td>
        <td>
          <div tablejsDataColClass="item">{{ purchase.item }}</div>
        </td>
      </tr>
    </tbody>
  </table>
</tablejs-grid>

If you inspect your td div tags, you will see the classes ‘name’ and ‘item’ are still applied to those tags.

3. Link Column Data: After this, we want to make each of these td div elements correspond to a th element. To achieve this, we will place a tablejsDataColClasses directive on the related th element. Supply the tablejsDataColClasses directive with the class name(s) of the td div element you want to have tied to each th element.

(Note: tablejsDatColClasses can be linked to multiple columns). Please reference the Nested Headers example for more information.

<tablejs-grid>
  <table>
    <thead>
      <tr tablejsGridRow>
        <th tablejsDataColClasses="name">
          <div>Name</div>
        </th>
        <th tablejsDataColClasses="item">
          <div>Purchase</div>
        </th>
      </tr>
    </thead>

    <tbody>
      <tr *ngFor="let purchase of purchases" tablejsGridRow>
        <td>
          <div tablejsDataColClass="name">{{ purchase.name }}</div>
        </td>
        <td>
          <div tablejsDataColClass="item">{{ purchase.item }}</div>
        </td>
      </tr>
    </tbody>
  </table>
</tablejs-grid>

TableJS will now start applying styles to your rows. By default, rows will be calculated based in pixels. In our case, we want to resize in percentage. We can achieve this in one of two ways.

  • Preferred method: Add initialWidth directives on your td div elements to tell TableJS the starting widths of these elements, and include a width in percentage. If we were resizing in pixels, it is preferred we do this step as well. Doing so will allow TableJS to skip a draw call necessary to get the starting width of each of these elements.

    <td>
      <div tablejsDataColClass="name" initialWidth="30%"></div>
    </td>
    
  • Alternative method: Set the resizeColumnWidthByPercent directive on the tablejs-grid element to true:

    <tablejs-grid [resizeColumnWidthByPercent]="true">
      ...
    </tablejs-grid>
    

Our HTML should now look something like this:

<tablejs-grid [resizeColumnWidthByPercent]="true">
  <table>
    <thead>
      <tr tablejsGridRow>
        <th tablejsDataColClasses="name">
          <div>Name</div>
        </th>
        <th tablejsDataColClasses="item">
          <div>Purchase</div>
        </th>
      </tr>
    </thead>

    <tbody>
      <tr *ngFor="let purchase of purchases" tablejsGridRow>
        <td>
          <div tablejsDataColClass="name" initialWidth="30%">
          {{ purchase.name }}
          </div>
        </td>
        <td>
          <div tablejsDataColClass="item" initialWidth="70%">
          {{ purchase.item }}
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</tablejs-grid>

4. Declare viewport: The viewport for TableJS should be the tbody tag. Place the tablejsViewport directive on the tbody element to tell TableJS where the content of the table will reside.

<tablejs-grid [resizeColumnWidthByPercent]="true">
  <table>
    <thead>
      <tr tablejsGridRow>
        <th tablejsDataColClasses="name">
          <div>Name</div>
        </th>
        <th tablejsDataColClasses="item">
          <div>Purchase</div>
        </th>
      </tr>
    </thead>

    <tbody tablejsViewport>
      <tr *ngFor="let purchase of purchases" tablejsGridRow>
        <td>
          <div tablejsDataColClass="name" initialWidth="30%">
          {{ purchase.name }}
          </div>
        </td>
        <td>
          <div tablejsDataColClass="item" initialWidth="70%">
          {{ purchase.item }}
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</tablejs-grid>

TableJS initialization is now complete! See the example below for the TableJS initialization code.

Example of Getting Started

Open Example in StackBlitz

Reorder

Implementation

For column reordering, we will continue developing our table from the Getting Started section.

<tablejs-grid [resizeColumnWidthByPercent]="true">
  <table>
    <thead>
      <tr tablejsGridRow>
        <th tablejsDataColClasses="name">
          <div>Name</div>
        </th>
        <th tablejsDataColClasses="item">
          <div>Purchase</div>
        </th>
      </tr>
    </thead>

    <tbody tablejsViewport>
      <tr *ngFor="let purchase of purchases" tablejsGridRow>
        <td>
          <div tablejsDataColClass="name" initialWidth="30%">
          {{ purchase.name }}
          </div>
        </td>
        <td>
          <div tablejsDataColClass="item" initialWidth="70%">
          {{ purchase.item }}
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</tablejs-grid>

We will need to tell TableJS which columns we want to be able to reorder. We do this by adding the reorderCol directive to each th element.

<tablejs-grid [resizeColumnWidthByPercent]="true">
  <table>
    <thead>
      <tr tablejsGridRow>
        <th tablejsDataColClasses="name" reorderCol>
            <div>Name</div>
        </th>
        <th tablejsDataColClasses="item" reorderCol>
            <div>Purchase</div>
        </th>
      </tr>
    </thead>

    <tbody tablejsViewport>
      <tr *ngFor="let purchase of purchases" tablejsGridRow>
        <td>
          <div tablejsDataColClass="name" initialWidth="30%">
          {{ purchase.name }}
          </div>
        </td>
        <td>
          <div tablejsDataColClass="item" initialWidth="70%">
          {{ purchase.item }}
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</tablejs-grid>

We will next want to designate which element to use as the grip for reordering. Let’s add a FontAwesome i element to tell the user which elements will reorder the columns. Add the reorderGrip directive to each i element to give it reorder functionality.

Note: You may need to add the following script to your index.html file if you don’t already have FontAwesome imported:

<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
<tablejs-grid [resizeColumnWidthByPercent]="true">
  <table>
    <thead>
      <tr tablejsGridRow>
        <th tablejsDataColClasses="name" reorderCol>
            <div>
                <i class="fas fa-ellipsis-v" reorderGrip></i>
                Name
            </div>
        </th>
        <th tablejsDataColClasses="item" reorderCol>
            <div>
                <i class="fas fa-ellipsis-v" reorderGrip></i>
                Purchase
            </div>
        </th>
      </tr>
    </thead>

    <tbody tablejsViewport>
      <tr *ngFor="let purchase of purchases" tablejsGridRow>
        <td>
          <div tablejsDataColClass="name" initialWidth="30%">
          {{ purchase.name }}
          </div>
        </td>
        <td>
          <div tablejsDataColClass="item" initialWidth="70%">
          {{ purchase.item }}
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</tablejs-grid>

Let’s add some CSS to style the reorder grips a bit more!

.fa-ellipsis-v {
  position: absolute;
  left: 12px;
  top: 10px;
  cursor: pointer;
}


Your columns can now be reordered and should look like this!

Reorder Columns

TableJS also adds classes to columns that can be styled to give a user visual cues about how the table is being reordered. While the user is reordering a column, thehighlight-left class will be added to a column as the user is hovering over the left half of a column, and the highlight-right classes will be added when the user is hovering over the right half of a column. Let’s add some styles to these classes so the user knows how the column is being reordered.

th,
td {
  &[reorderCol] {
    &.highlight-left {
      position: relative;
      overflow: visible !important;
    }
    &.highlight-left > div,
    &.highlight-right > div {
      overflow: visible !important;
    }
    &.highlight-left > div:after,
    &.highlight-right > div:after {
      box-sizing: border-box;
      position: absolute;
      display: block;
      background-color: rgba(122, 122, 122, 0.05);
      height: 404px;
      width: 100%;
      top: 0px;
      left: 0px;
      z-index: 5;
    }

    &.highlight-left > div {
      &:after {
        font-family: 'Font Awesome 5 Free';
        content: '';
        font-weight: 900;
        border-left-style: solid;
        border-left-width: 1px;
        border-left-color: #000000;
        padding-left: 10px;
        text-align: left;

        box-shadow: inset 3px 0 3px -3px rgba(0, 0, 0, 0.4);
      }
      &.sort-icon {
        &:after {
          display: none;
        }
      }
    }
    &.highlight-right > div {
      &:after {
        font-family: 'Font Awesome 5 Free';
        content: '';
        font-weight: 900;
        border-right-style: solid;
        border-right-width: 1px;
        border-right-color: #000000;
        padding-right: 10px;
        text-align: right;

        box-shadow: inset -3px 0 3px -3px rgba(0, 0, 0, 0.4);
      }
      &.sort-icon {
        &:after {
          display: none;
        }
      }
    }
  }
}

In addition to highlights, TableJS will also make an image representation of a th element when it is dragged to give the user a visual cue as to which column is being reordered. You can filter out the elements you do not want to be copied into this image by using the dragDropFilter function. In our example, we will filter out all i elements.

dragDropFilter(el: HTMLElement): boolean {
    return el.tagName !== 'I';
}

Now when we reorder, we should see something like this:

Reorder Columns Hover

That’s it! You have now implemented reorder functionality.

Example of reordering columns

Open Example in StackBlitz

Resize in Percentage

Implementation

For resizing in percentage, let’s continue developing from our Getting Started section.

<tablejs-grid [resizeColumnWidthByPercent]="true">
  <table>
    <thead>
      <tr tablejsGridRow>
        <th tablejsDataColClasses="name">
          <div>Name</div>
        </th>
        <th tablejsDataColClasses="item">
          <div>Purchase</div>
        </th>
      </tr>
    </thead>

    <tbody tablejsViewport>
      <tr *ngFor="let purchase of purchases" tablejsGridRow>
        <td>
          <div tablejsDataColClass="name" initialWidth="30%">
          {{ purchase.name }}
          </div>
        </td>
        <td>
          <div tablejsDataColClass="item" initialWidth="70%">
          {{ purchase.item }}
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</tablejs-grid>

To implement resize functionality, we can place a resizableGrip directive on any element to designate it as a column’s resizing handler. In our case, we will add the resizableGrip functionality on a FontAwesome i tag.

Note: You may need to add the following script to your index.html file if you don’t already have FontAwesome imported:

<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">

Our Updated HTML should look like the following:

<tablejs-grid [resizeColumnWidthByPercent]="true">
  <table>
    <thead>
      <tr tablejsGridRow>
        <th tablejsDataColClasses="name">
          <div>Name</div>
          <i class="fas fa-sort resize" resizableGrip></i>
        </th>
        <th tablejsDataColClasses="item">
          <div>Purchase</div>
          <i class="fas fa-sort resize" resizableGrip></i>
        </th>
      </tr>
    </thead>

    <tbody tablejsViewport>
      <tr *ngFor="let purchase of purchases" tablejsGridRow>
        <td>
          <div tablejsDataColClass="name" initialWidth="30%">
          {{ purchase.name }}
          </div>
        </td>
        <td>
          <div tablejsDataColClass="item" initialWidth="70%">
          {{ purchase.item }}
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</tablejs-grid>

Let’s add some CSS to style the resize grips a bit more!

.fa-sort.resize {
  transform: rotate(90deg);
  position: absolute;
  right: 12px;
  top: 10px;
  cursor: pointer;
}


Voila! Your columns can now be resized and should look like this!

Resize Columns in Percent

Example of resizing columns calculated in percent

Open Example in StackBlitz

Resize in Pixels

Implementation

For resizing in pixels, let’s continue developing from our Getting Started section.

<tablejs-grid [resizeColumnWidthByPercent]="true">
  <table>
    <thead>
      <tr tablejsGridRow>
        <th tablejsDataColClasses="name">
          <div>Name</div>
        </th>
        <th tablejsDataColClasses="item">
          <div>Purchase</div>
        </th>
      </tr>
    </thead>

    <tbody tablejsViewport>
      <tr *ngFor="let purchase of purchases" tablejsGridRow>
        <td>
          <div tablejsDataColClass="name" initialWidth="30%">
          {{ purchase.name }}
          </div>
        </td>
        <td>
          <div tablejsDataColClass="item" initialWidth="70%">
          {{ purchase.item }}
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</tablejs-grid>

We will need to make some adjustments to the HTML and CSS to display the table using pixels. We can remove the resizeColumnWidthByPercent directive altogether as it is only needed when calculating in percentages. Next we will change our initialWidth values to numbers instead of percentages.

<tablejs-grid>
  <table>
    <thead>
      <tr tablejsGridRow>
        <th tablejsDataColClasses="name">
          <div>Name</div>
        </th>
        <th tablejsDataColClasses="item">
          <div>Purchase</div>
        </th>
      </tr>
    </thead>

    <tbody tablejsViewport>
      <tr *ngFor="let purchase of purchases" tablejsGridRow>
        <td>
          <div tablejsDataColClass="name" initialWidth="250">
          {{ purchase.name }}
          </div>
        </td>
        <td>
          <div tablejsDataColClass="item" initialWidth="400">
          {{ purchase.item }}
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</tablejs-grid>

Let’s make sure our CSS classes match the initialWidth values.

td {
    background-color: #ffffff;
    div {
        font-size: 13px;

        &.name {
            width: 250px;
        }
        &.item {
            width: 400px;
        }
    }
}


To implement resize functionality, we can place a resizableGrip directive on any element to designate it as a column’s resizing handler. In our case, we will add the resizableGrip functionality on a FontAwesome i tag.

Note: You may need to add the following script to your index.html file if you don’t already have FontAwesome imported:

<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">

Our Updated HTML should look like the following:

<tablejs-grid>
  <table>
    <thead>
      <tr tablejsGridRow>
        <th tablejsDataColClasses="name">
          <div>Name</div>
          <i class="fas fa-sort resize" resizableGrip></i>
        </th>
        <th tablejsDataColClasses="item">
          <div>Purchase</div>
          <i class="fas fa-sort resize" resizableGrip></i>
        </th>
      </tr>
    </thead>

    <tbody tablejsViewport>
      <tr *ngFor="let purchase of purchases" tablejsGridRow>
        <td>
          <div tablejsDataColClass="name" initialWidth="250">
          {{ purchase.name }}
          </div>
        </td>
        <td>
          <div tablejsDataColClass="item" initialWidth="400">
          {{ purchase.item }}
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</tablejs-grid>

Let’s add some CSS to style the resize grips a bit more!

.fa-sort.resize {
  transform: rotate(90deg);
  position: absolute;
  right: 12px;
  top: 10px;
  cursor: pointer;
}


Voila! Your columns can now be resized and should look like this!

Resize Columns in Pixels

Example of resizing columns calculated in pixels

Open Example in StackBlitz

Virtual Scrolling

Example of infinite virtual scrolling

Open Example in StackBlitz

Event Handling

Implementation

For event handling, we will continue developing our table from the Getting Started section. We will utilize the CSS classes added in the previous Resize in Pixels and Reorder examples above.

<tablejs-grid [resizeColumnWidthByPercent]="true">
  <table>
    <thead>
      <tr tablejsGridRow>
        <th tablejsDataColClasses="name">
          <div>Name</div>
        </th>
        <th tablejsDataColClasses="item">
          <div>Purchase</div>
        </th>
      </tr>
    </thead>

    <tbody tablejsViewport>
      <tr *ngFor="let purchase of purchases" tablejsGridRow>
        <td>
          <div tablejsDataColClass="name" initialWidth="30%">
          {{ purchase.name }}
          </div>
        </td>
        <td>
          <div tablejsDataColClass="item" initialWidth="70%">
          {{ purchase.item }}
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</tablejs-grid>

To enable event handling, we will need to first reintroduce the resizableGrip, reorderGrip, and reorderCol directives. These allow users access to the resize and reorder functionality. Let’s begin by adding the reorderCol directive to each of our th tags. This will enable reorder and resize functionality. Next we will place reorderGrip directives on any element that we want to use as a reorder handle for the user. In this case, we are adding the directive to the <i class="fas fa-ellipsis-v"></i> tags. We do the same thing with the resizableGrip to provide a handle for resizing. These are placed on the <i class="fas fa-sort resize"></i> tags.

<tablejs-grid [resizeColumnWidthByPercent]="true">
  <table>
    <thead>
      <tr tablejsGridRow>
        <th tablejsDataColClasses="name" reorderCol>
          <div>
            <i class="fas fa-ellipsis-v" reorderGrip></i>
            Name
            <i class="fas fa-sort resize" resizableGrip></i>
          </div>
        </th>
        <th tablejsDataColClasses="item" reorderCol>
          <div>
            <i class="fas fa-ellipsis-v" reorderGrip></i>
            Purchase
            <i class="fas fa-sort resize" resizableGrip></i>
          </div>
        </th>
      </tr>
    </thead>

    <tbody tablejsViewport>
      <tr *ngFor="let purchase of purchases" tablejsGridRow>
        <td>
          <div tablejsDataColClass="name" initialWidth="30%">
          {{ purchase.name }}
          </div>
        </td>
        <td>
          <div tablejsDataColClass="item" initialWidth="70%">
          {{ purchase.item }}
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</tablejs-grid>

Next, we will assign some CSS for our reorder and resize handles.

.fa-sort.resize {
  transform: rotate(90deg);
  position: absolute;
  right: 12px;
  top: 10px;
  cursor: pointer;
}

.fa-ellipsis-v {
  position: absolute;
  left: 12px;
  top: 10px;
  cursor: pointer;
}

Now that we have the visualization set up, we can start adding an event Output to catch the event when the user starts resizing a column. Let’s place a columnResizeStart Output on our tablejs-grid element to catch the event when the user begins resizing a column. We need to declare a function to catch the event. In this case, we will name our function columnResizeStart and pass in the $event object.

<tablejs-grid 
  [resizeColumnWidthByPercent]="true"
  (columnResizeStart)="columnResizeStart($event)">
  <table> ... </table>
</tablejs-grid>

The function below is the function supplied to our HTML template that catches the ColumnResizeEvent that is fired when the user starts resizing a column.

columnResizeStart(e: ColumnResizeEvent) {
  console.log('column resize started: \n', e);
}

We employ the same technique to catch the events triggered while the user is resizing the column. Add the columnResize Output to our template file with a function with the same name. This function will be called when each resize event is triggered.

<tablejs-grid 
  [resizeColumnWidthByPercent]="true"
  (columnResizeStart)="columnResizeStart($event)"
  (columnResize)="columnResize($event)">
  <table> ... </table>
</tablejs-grid>

The function below will catch every column resize event.

columnResize(e: ColumnResizeEvent) {
  console.log('column resizing: \n', e);
}

We have implemented catching the resize start and resize events. Let’s add one more event handler for catching the resize end event. Add the columnResizeEnd Output with a corresponding function to catch the resize column end event.

<tablejs-grid 
  [resizeColumnWidthByPercent]="true"
  (columnResizeStart)="columnResizeStart($event)"
  (columnResize)="columnResize($event)"
  (columnResizeEnd)="columnResizeEnd($event)">
  <table> ... </table>
</tablejs-grid>

Let’s place the columnResizeEnd function in the Typescript to catch the column resize end event.

columnResizeEnd(e: ColumnResizeEvent) {
  console.log('column resize ended: \n', e);
}

We use the same implementation for catching column reordering events as we do for column resizing events. Let’s start by adding columnReorderStart to our template and pass a function for catching the reorder start event.

<tablejs-grid 
  [resizeColumnWidthByPercent]="true"
  (columnReorderStart)="columnReorderStart($event)">
  <table> ... </table>
</tablejs-grid>

Then we place our columnReorderStart function in the Typescript to catch when reordering begins. A ColumnReorderEvent object will be emitted into our function when column reordering begins.

columnReorderStart(e: ColumnReorderEvent) {
  console.log('column reorder started: \n', e);
}

Next, we add columnReorder to handle when the user is moving the column around during reorder.

<tablejs-grid 
  [resizeColumnWidthByPercent]="true"
  (columnReorderStart)="columnReorderStart($event)"
  (columnReorder)="columnReorder($event)">
  <table> ... </table>
</tablejs-grid>

Again, we will include a function in our Typescript to catch the reorder event.

columnReorder(e: ColumnReorderEvent) {
  console.log('column reordering: \n', e);
}

Finally, we want to catch the column reorder end event. We will add one final Output to our tablejs-grid element. We add columnReorderEnd to catch when a user completes reordering a column, and give it a function to catch this event in the Typescript.

<tablejs-grid 
  [resizeColumnWidthByPercent]="true"
  (columnReorderStart)="columnReorderStart($event)"
  (columnReorder)="columnReorder($event)"
  (columnReorderEnd)="columnReorderEnd($event)">
  <table> ... </table>
</tablejs-grid>

The function below will catch all the events fired for when the user completes a column reorder.

columnReorderEnd(e: ColumnReorderEvent) {
  console.log('column reorder ended: \n', e);
}

And below is our full HTML event method implementation.

<tablejs-grid 
  [resizeColumnWidthByPercent]="true"
  (columnResizeStart)="columnResizeStart($event)"
  (columnResize)="columnResize($event)"
  (columnResizeEnd)="columnResizeEnd($event)"
  (columnReorderStart)="columnReorderStart($event)"
  (columnReorder)="columnReorder($event)"
  (columnReorderEnd)="columnReorderEnd($event)">
  <table>
    <thead>
      <tr tablejsGridRow>
        <th tablejsDataColClasses="name" reorderCol>
          <div>
            <i class="fas fa-ellipsis-v" reorderGrip></i>
            Name
            <i class="fas fa-sort resize" resizableGrip></i>
          </div>
        </th>
        <th tablejsDataColClasses="item" reorderCol>
          <div>
            <i class="fas fa-ellipsis-v" reorderGrip></i>
            Purchase
            <i class="fas fa-sort resize" resizableGrip></i>
          </div>
        </th>
      </tr>
    </thead>

    <tbody tablejsViewport>
      <tr *ngFor="let purchase of purchases" tablejsGridRow>
        <td>
          <div tablejsDataColClass="name" initialWidth="30%">
          {{ purchase.name }}
          </div>
        </td>
        <td>
          <div tablejsDataColClass="item" initialWidth="70%">
          {{ purchase.item }}
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</tablejs-grid>

With all our resize and reorder Outputs defined, our Typescript should look like the following:

import { ColumnResizeEvent, ColumnReorderEvent } from '@transunion-ui/tablejs';

export class EventHandling {
  columnResizeStart(e: ColumnResizeEvent) {
    console.log('column resize started: \n', e);
  }
  columnResize(e: ColumnResizeEvent) {
    console.log('column resizing: \n', e);
  }
  columnResizeEnd(e: ColumnResizeEvent) {
    console.log('column resize ended: \n', e);
  }

  columnReorderStart(e: ColumnReorderEvent) {
    console.log('column reorder started: \n', e);
  }
  columnReorder(e: ColumnReorderEvent) {
    console.log('column reordering: \n', e);
  }
  columnReorderEnd(e: ColumnReorderEvent) {
    console.log('column reorder ended: \n', e);
  }
}

That’s it! You have now implemented event handling functionality.

Example of resize and reorder event handling

Open Example in StackBlitz

Sort

Example of data sorting in tables

Open Example in StackBlitz

Filter

Implementation

For column filtering, we will continue developing our table from the Getting Started section.

<tablejs-grid [resizeColumnWidthByPercent]="true">
  <table>
    <thead>
      <tr tablejsGridRow>
        <th tablejsDataColClasses="name">
          <div>Name</div>
        </th>
        <th tablejsDataColClasses="item">
          <div>Purchase</div>
        </th>
      </tr>
    </thead>

    <tbody tablejsViewport>
      <tr *ngFor="let purchase of purchases" tablejsGridRow>
        <td>
          <div tablejsDataColClass="name" initialWidth="30%">
          {{ purchase.name }}
          </div>
        </td>
        <td>
          <div tablejsDataColClass="item" initialWidth="70%">
          {{ purchase.item }}
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</tablejs-grid>

In this implementation we will be filtering by text. Let’s give the user an input box for typing in text that can be used to filter.

<div>
    Filter Value
    <input type="text" name="filterTextBox" (input)="filterByText($event)" />
</div>

The method below will be bound to an (input) event in our HTML above. The filterByText function will take the user’s given input value, and filter the grid accordingly. We will add more logic to this function once the rest of our code is fleshed out.

filterByText(e: any): void {
}

Our HTML should now look like this with our filtering input added.

<div>
    Filter Value
    <input type="text" name="filterTextBox" (input)="filterByText($event)" />
</div>
<tablejs-grid [resizeColumnWidthByPercent]="true">
  <table>
    <thead>
      <tr tablejsGridRow>
        <th tablejsDataColClasses="name">
          <div>Name</div>
        </th>
        <th tablejsDataColClasses="item">
          <div>Purchase</div>
        </th>
      </tr>
    </thead>

    <tbody tablejsViewport>
      <tr *ngFor="let purchase of purchases" tablejsGridRow>
        <td>
          <div tablejsDataColClass="name" initialWidth="30%">
          {{ purchase.name }}
          </div>
        </td>
        <td>
          <div tablejsDataColClass="item" initialWidth="70%">
          {{ purchase.item }}
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</tablejs-grid>

Now that our HTML is defined, let’s begin working on the rest of our Typescript implementation. Let’s continue by declaring our list of purchases.

export class FilterComponent {
    purchases: any[] = [
    {
      name: 'John Hughes',
      purchase: 'Cameras'
    },
    {
      name: 'Jack Hughes',
      purchase: 'Hockey Stick'
    },
    {
      name: 'Tony Hawk',
      purchase: 'Skateboard'
    }
    ];

    constructor() {}

    filterByText(e: any): void {
    }
}

Next, we need to inject a FilterSortService variable in our constructor and declare a FilterOptions variable to use as a set of options we want to use for filtering.

import { FilterOptions, FilterSortService } from '@transunion-ui/tablejs';

export class FilterComponent {
    filterOptions: FilterOptions;
    purchases: any[] = [
    {
      name: 'John Hughes',
      purchase: 'Cameras'
    },
    {
      name: 'Jack Hughes',
      purchase: 'Hockey Stick'
    },
    {
      name: 'Tony Hawk',
      purchase: 'Skateboard'
    }
    ];

    constructor(public filterSortService: FilterSortService) {}

    filterByText(e: any): void {
    }
}

With our variables initialized, we can define the FilterOptions filtering logic. Here we are using our setFilterOptions method to instantiate our filterOptions variable with some options. We will filter on the name and purchase properties of our purchases, and then set our options so that the name and purchase properties will be checked for containing a specific string. For this, we will use the FilterComparator.CONTAINS_STRING function as our filter comparator. By using MatchType.ANY, the object will be returned if either of the name or purchase properties contain the string. You can use MatchType.ALL if you want to have all properties contain the string in order to be filtered.

import { FilterOptions, FilterComparator, FilterSortService } from '@transunion-ui/tablejs';

export class FilterComponent {
    filterOptions: FilterOptions;
    purchases: any[] = [
    {
      name: 'John Hughes',
      purchase: 'Cameras'
    },
    {
      name: 'Jack Hughes',
      purchase: 'Hockey Stick'
    },
    {
      name: 'Tony Hawk',
      purchase: 'Skateboard'
    }
    ];

    constructor(public filterSortService: FilterSortService) {
      this.setFilterOptions();
    }

    setFilterOptions(): void {
      this.filterOptions = new FilterOptions(
        ['name', 'purchase'],
        FilterComparator.CONTAINS_STRING,
        '',
        MatchType.ANY,
        true
      );
    }

    filterByText(e: any): void {
    }
}

With our FilterOptions defined, we can utilize our FilterSortService to filter out our desired purchases. This is done in our filterItems method below, where our defined FilterOptions and purchases array are plugged into our filterSortService.filterAndSortItems method.

import { FilterOptions, FilterComparator, FilterSortService } from '@transunion-ui/tablejs';

export class FilterComponent {
    filterOptions: FilterOptions;
    purchases: any[] = [
    {
      name: 'John Hughes',
      purchase: 'Cameras'
    },
    {
      name: 'Jack Hughes',
      purchase: 'Hockey Stick'
    },
    {
      name: 'Tony Hawk',
      purchase: 'Skateboard'
    }
    ];

    constructor(public filterSortService: FilterSortService) {
      this.setFilterOptions();
    }

    setFilterOptions(): void {
      this.filterOptions = new FilterOptions(
        ['name', 'purchase'],
        FilterComparator.CONTAINS_STRING,
        '',
        MatchType.ANY,
        true
      );
    }

    filterItems(): void {
      let filteredItems: any[] = this.filterSortService.filterAndSortItems(
        this.purchases,
        null,
        this.filterOptions
      );
      console.log(filteredItems);
    }

    filterByText(e: any): void {
    }
}

Finally, let’s add some logic to our filterByText method that will set the value to filter on based on the user’s input.

import { FilterOptions, FilterComparator, FilterSortService } from '@transunion-ui/tablejs';

export class FilterComponent {
    filterOptions: FilterOptions;
    purchases: any[] = [
    {
      name: 'John Hughes',
      purchase: 'Cameras'
    },
    {
      name: 'Jack Hughes',
      purchase: 'Hockey Stick'
    },
    {
      name: 'Tony Hawk',
      purchase: 'Skateboard'
    }
    ];

    constructor(public filterSortService: FilterSortService) {
      this.setFilterOptions();
    }

    setFilterOptions(): void {
      this.filterOptions = new FilterOptions(
        ['name', 'purchase'],
        FilterComparator.CONTAINS_STRING,
        '',
        MatchType.ANY,
        true
      );
    }

    filterItems(): void {
      this.purchases = this.filterSortService.filterAndSortItems(
        this.purchases,
        null,
        this.filterOptions
      );
      console.log(this.purchases);
    }

    filterByText(e: any): void {
      this.filterOptions!.filterValue = e.target.value;
      this.filterItems();
    }
}

All done! You have now implemented some filtering functionality.

Example of data filtering in tables

Open Example in StackBlitz

Editable Cells

Example of realtime editing and validating cell data

Open Example in StackBlitz

Linked Tables

Example of linking resize and reorder functionality between multiple tables

Open Example in StackBlitz

Nested Headers

Example of multi-tiered headers

Open Example in StackBlitz

Pagination

Example of pagination

Open Example in StackBlitz

Tree View

Example of tree view manipulation

Open Example in StackBlitz

### Getting Started #### Installation `$ npm install --save @transunion-ui/tablejs` #### Implementation ##### Set Up 1. Import the `TablejsModule` into your app.module.ts file. ```javascript import { TablejsModule } from '@transunion-ui/tablejs'; ``` 2. Add the `TablejsModule` to the list of imports in your app.module.ts file. ```javascript @NgModule ({ declarations: [ ... ], imports: [ TablejsModule ], providers: [ ... ] }); ``` 3. Let's create a component and place some HTML table markup in the component's Typescript file. Consider the following HTML: ```html
Name
Purchase
The Purchaser's Name
The item purchased
``` 4. And its corresponding SCSS: ```CSS div { position: relative; padding: 0px; margin: 0; box-sizing: border-box; } table { font-family: Lato; table-layout: fixed; border-collapse: collapse; border-spacing: 0px; padding: 0; margin: 0; width: 100%; thead { display: table-header-group; } tbody { display: table-row-group; } thead, tbody { position: relative; left: 0px; top: 0px; width: 100%; tr { display: grid; grid-template-columns: 30% 70%; th, td { position: relative; border: 1px solid #e9e9e9; height: 36px; width: 100%; div { position: relative; padding: 8px; width: 100%; height: 100%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } } th { text-align: center; background-color: #555555; div { color: #ffffff; font-size: 15px; } } td { background-color: #ffffff; div { font-size: 13px; &.name { width: 100%; } &.item { width: 100%; } } } } } } ``` You should now have a table that looks something like this: 5. Now that we have a simple table in place, let's display some dynamic data in the table. Create an array of items in your component's Typescript file for displaying in the table. ```javascript purchases: any[] = [ { name: 'John Hughes', item: 'Cameras', }, { name: 'Mickey Mouse', item: 'Whistles', }, { name: 'Amelia Earhart', item: 'Goggles', }, { name: 'Godzilla', item: 'Breath Mints', }, ]; ``` To display this data in the template, we will use the `ngFor` directive to iterate over each item in the array. Then replace our static name and purchase item with the corresponding variables from our array. ```HTML <tr *ngFor="let purchase of purchases"> </tr>
Name
Purchase
``` #####TableJS Initialization If we want to enable resize or reordering, we will need to supply TableJS with row, column, and viewport information. 1. **Register rows:** TableJS will be able to track row information once we place the `tablejsGridRow` directive on each `tr` element. ```HTML <tr *ngFor="let purchase of purchases" tablejsGridRow> </tr>
Name
Purchase
``` Great! TableJS can now register your rows! 2. **Declare column data:** Next, we will need to tie the columns from the `thead` tag to the columns in the `tbody` tag for sizing calculations. TableJS uses CSS classes to accomplish this. Let's replace the `class` attribute on the `td div` element with the `tablejsDataColClass`directive. The class name supplied to the `tablejsDataColClass` directive will automatically placed on the `td div`. ```HTML <tr *ngFor="let purchase of purchases" tablejsGridRow> </tr>
Name
Purchase
``` If you inspect your `td div` tags, you will see the classes 'name' and 'item' are still applied to those tags. 3. **Link Column Data:** After this, we want to make each of these `td div` elements correspond to a `th` element. To achieve this, we will place a `tablejsDataColClasses` directive on the related `th` element. Supply the `tablejsDataColClasses` directive with the class name(s) of the `td div` element you want to have tied to each `th` element. **(Note: tablejsDatColClasses can be linked to multiple columns). Please reference the Nested Headers example for more information.** ```HTML <tr *ngFor="let purchase of purchases" tablejsGridRow> </tr>
Name
Purchase
``` TableJS will now start applying styles to your rows. By default, rows will be calculated based in pixels. In our case, we want to resize in percentage. We can achieve this in one of two ways. 1. **Preferred method:** Add `initialWidth` directives on your `td div` elements to tell TableJS the starting widths of these elements, and include a width in percentage. If we were resizing in pixels, it is preferred we do this step as well. Doing so will allow TableJS to skip a draw call necessary to get the starting width of each of these elements. ```HTML
``` 2. **Alternative method:** Set the `resizeColumnWidthByPercent` directive on the `tablejs-grid` element to true: ```HTML <tablejs-grid [resizeColumnWidthByPercent]="true"> ... </tablejs-grid> ``` Our HTML should now look something like this: ```HTML <tablejs-grid [resizeColumnWidthByPercent]="true"> <tr *ngFor="let purchase of purchases" tablejsGridRow> </tr>
Name
Purchase
</tablejs-grid> ``` 4. **Declare viewport:** The viewport for TableJS should be the `tbody` tag. Place the `tablejsViewport` directive on the `tbody` element to tell TableJS where the content of the table will reside. ```HTML <tablejs-grid [resizeColumnWidthByPercent]="true"> <tr *ngFor="let purchase of purchases" tablejsGridRow> </tr>
Name
Purchase
</tablejs-grid> ``` TableJS initialization is now complete! See the example below for the TableJS initialization code.

Example of Getting Started

Open Example in StackBlitz

Print

Example of table printing

Open Example in StackBlitz