# Data Tables

The mx-table component can be used one of two ways:

  • by providing an array of data via the rows prop, allowing the table to automatically generate rows and cells
  • by templating the rows and cells manually using mx-table-row and mx-table-cell components

# Basic data-driven table

To create a basic data table with auto-generated rows, provide a rows prop containing your data, as well as a columns prop, which is an array of ITableColumn objects that define the tables columns.

<mx-table
  paginate="false"
  :rows.prop="beatles"
  :columns.prop="[
    { property: 'firstName', heading: 'First Name' },
    { property: 'lastName', heading: 'Last Name' },
    { property: 'credits', heading: 'Song Credits', type: 'number' },
    { property: 'birthdate', heading: 'Birthdate', type: 'date' },
    { property: 'eyeColor', heading: 'Eye Color', sortable: false }
  ]"
/>
1
2
3
4
5
6
7
8
9
10
const beatles = [
  {
    firstName: 'John',
    lastName: 'Lennon',
    credits: 90,
    birthdate: new Date(1940, 9, 9),
    isLeftHanded: false,
    eyeColor: 'Brown',
  },
  {
    firstName: 'Paul',
    lastName: 'McCartney',
    credits: 88,
    birthdate: new Date(1942, 5, 18),
    isLeftHanded: true,
    eyeColor: 'Hazel',
  },
  {
    firstName: 'George',
    lastName: 'Harrison',
    credits: 22,
    birthdate: new Date(1943, 1, 25),
    isLeftHanded: false,
    eyeColor: 'Brown',
  },
  {
    firstName: 'Ringo',
    lastName: 'Starr',
    credits: 2,
    birthdate: new Date(1940, 6, 7),
    isLeftHanded: false,
    eyeColor: 'Blue',
  }
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# Pagination

By default, the paginate prop is set to true, which will break the rows up into pages that are limited to rowsPerPage (the default is 10).

<mx-table
  rows-per-page="5"
  :rows-per-page-options.prop="[5, 10, 25, 50]"
  :rows.prop="albums"
  :columns.prop="[
    { property: 'entertainer', heading: 'Artist', sortable: false },
    { property: 'album', heading: 'Album' },
    { property: 'releasedate', heading: 'Release Date', type: 'date' },
    { property: 'label', heading: 'Label' },
  ]"
/>
1
2
3
4
5
6
7
8
9
10

# Cell value getters

You can use the getValue property of ITableColumn to pass a value getter for a column. This can even return HTML.

<mx-table
  auto-width
  paginate="false"
  hoverable="false"
  :rows.prop="beatles"
  :columns.prop="[
    { property: 'firstName', heading: 'First Name' },
    { property: 'lastName', heading: 'Last Name' },
    { property: 'isLeftHanded', heading: 'Handedness', getValue: buildBadge },
  ]"
/>
1
2
3
4
5
6
7
8
9
10
buildBadge(row) {
  const handedness = row.isLeftHanded ? 'Left' : 'Right'
  const color = row.isLeftHanded ? 'bg-purple-300' : 'bg-blue-300'
  return `<mx-badge squared badge-class="${color}" value="${handedness}"></mx-badge>`
},
1
2
3
4

# Row actions

The getRowActions prop is function that accepts the row as a parameter and returns an array of props to generate Menu Items (or a Text Button if there is only one action). For the inner text of the menu item / button, pass it as a value property.

<mx-table
  :rows.prop="beatles"
  :columns.prop="[
    { property: 'firstName', heading: 'First Name' },
    { property: 'lastName', heading: 'Last Name' },
    { property: 'credits', heading: 'Song Credits', type: 'number' }
  ]"
  :get-row-actions.prop="row => ([
    { value: 'Edit ' + row.firstName, onClick: () => clickHandler(row) },
    { value: 'Delete', onClick: () => clickHandler(row) }
  ])"
/>
<mx-table
  :rows.prop="beatles"
  :columns.prop="[
    { property: 'firstName', heading: 'First Name' },
    { property: 'lastName', heading: 'Last Name' },
    { property: 'birthdate', heading: 'Birthdate', type: 'date' },
    { property: 'credits', heading: 'Song Credits', type: 'number' }
  ]"
  :get-row-actions.prop="row => ([
    { value: 'Edit', icon: 'ph ph-pencil', onClick: () => clickHandler(row) }
  ])"
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# Row selection

Setting the checkable prop on a table adds checkboxes to the rows to allow selecting one or more rows. When this is set, each row must be given a unique identifier in order to track the selected rows. This can be done by providing a getRowId function that tells the table how to generate the rowId for each row.

<mx-table
  checkable
  :get-row-id.prop="row => row.id"
  :rows.prop="albums"
  :columns.prop="[
    { property: 'entertainer', heading: 'Artist', sortable: false },
    { property: 'album', heading: 'Album' },
    { property: 'releasedate', heading: 'Release Date', type: 'date' },
    { property: 'label', heading: 'Label' },
  ]"
/>
1
2
3
4
5
6
7
8
9
10

# Multi-row actions

The getMultiRowActions prop can be used to generate an action menu/button for the selected rows. The getMultiRowActions function receives the selected rowIds as its only parameter. Similar to the inline row actions, the function should return an array of Menu Item or Text Button prop objects with a value property for the inner text of the menu item.

<mx-table
  ref="multitable1"
  checkable
  :get-row-id.prop="row => row.firstName"
  :rows.prop="beatles"
  :columns.prop="[
    { property: 'firstName', heading: 'First Name' },
    { property: 'lastName', heading: 'Last Name' },
    { property: 'birthdate', heading: 'Birthdate', type: 'date' },
    { property: 'eyeColor', heading: 'Eye Color' },
  ]"
  :get-multi-row-actions.prop="rowIds => ([
    {
      value: `Merge ${rowIds.length > 1 ? rowIds.length : ''} rows`,
      disabled: rowIds.length < 2,
      onClick: () => multiRowClickHandler(rowIds)
    },
    { value: 'Delete', onClick: () => multiRowClickHandler(rowIds) },
  ])"
/>
<mx-table
  ref="multitable2"
  checkable
  :get-row-id.prop="row => row.firstName"
  :rows.prop="beatles"
  :columns.prop="[
    { property: 'firstName', heading: 'First Name' },
    { property: 'lastName', heading: 'Last Name' },
    { property: 'birthdate', heading: 'Birthdate', type: 'date' },
  ]"
  :get-multi-row-actions.prop="rowIds => ([
    { value: 'Delete', icon: 'ph ph-trash', onClick: () => multiRowClickHandler(rowIds) }
  ])"
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
async multiRowClickHandler(rowIds) {
  console.log(`Action selected with ${rowIds.length} row(s)!`)
  this.$refs.multitable1.checkNone()
  this.$refs.multitable2.checkNone()
},
1
2
3
4

# Custom layouts

Table rows can be manually templated using mx-table-row and mx-table-cell components within the table's default slot.

If you are relying on client-side sorting and pagination, you should iterate over the sorted/paginated array of rows that is emitted via the table's mxVisibleRowsChange event when creating your mx-table-row instances. This event fires once after the table first loads, as well as any time the sorting, pagination, or rows data is altered.

For checkable tables, you do not need to add the checkboxes to your custom layout; those will be added automatically. The action button/menu for each row is also generated for you if you provide an actions prop.

John Lennon 10/9/1940 Paul McCartney 6/18/1942 George Harrison 2/25/1943 Ringo Starr 7/7/1940
<mx-table
  checkable
  :rows.prop="beatles"
  :columns.prop="[
    { property: 'firstName', heading: 'First Name' },
    { property: 'lastName', heading: 'Last Name' },
    { property: 'birthdate', heading: 'Birthdate', type: 'date' },
    { property: 'eyeColor', heading: 'Eye Color' },
  ]"
  @mxVisibleRowsChange="e => visibleRows = e.detail"
>
  <div>
    <mx-table-row
      v-for="(row, i) in visibleRows"
      :key="row.firstName"
      :row-id="row.firstName"
      :actions.prop="[{ value: 'Delete', onClick: () => clickHandler(row) }]"
    >
      <mx-table-cell>{{ row.firstName }}</mx-table-cell>
      <mx-table-cell>{{ row.lastName }}</mx-table-cell>
      <mx-table-cell>{{ row.birthdate.toLocaleDateString() }}</mx-table-cell>
      <mx-table-cell>
        <mx-badge
          indicator
          :style="{ color: getEyeColorHex(row.eyeColor) }"
          :title="row.eyeColor"
        ></mx-badge>
      </mx-table-cell>
    </mx-table-row>
  </div>
</mx-table>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# Custom action column

It is possible to use mx-table-cell elements to lay out a custom action column that is not limited to a single button or menu. Set the isActionColumn property of that column to true to automatically give it a screenreader-only heading of "Action".

John Lennon 10/9/1940 Paul McCartney 6/18/1942 George Harrison 2/25/1943 Ringo Starr 7/7/1940
<mx-table
  :columns.prop="[
    { heading: 'First Name' },
    { heading: 'Last Name' },
    { heading: 'Birthdate', align: 'right' },
    { isActionColumn: true, align: 'right', cellClass: 'p-0' },
  ]"
>
  <div>
    <mx-table-row>
      <mx-table-cell>John</mx-table-cell>
      <mx-table-cell>Lennon</mx-table-cell>
      <mx-table-cell>10/9/1940</mx-table-cell>
      <mx-table-cell>
        <mx-icon-button el-aria-label="Like" icon="ph ph-heart" @click="clickHandler({ firstName: 'John' })"></mx-icon-button>
        <mx-icon-button el-aria-label="Delete" icon="ph ph-trash" @click="clickHandler({ firstName: 'John' })"></mx-icon-button>
      </mx-table-cell>
    </mx-table-row>
    <mx-table-row>
      <mx-table-cell>Paul</mx-table-cell>
      <mx-table-cell>McCartney</mx-table-cell>
      <mx-table-cell>6/18/1942</mx-table-cell>
      <mx-table-cell>
        <mx-icon-button el-aria-label="Like" icon="ph ph-heart" @click="clickHandler({ firstName: 'Paul' })"></mx-icon-button>
        <mx-icon-button el-aria-label="Delete" icon="ph ph-trash" @click="clickHandler({ firstName: 'Paul' })"></mx-icon-button>
      </mx-table-cell>
    </mx-table-row>
    <mx-table-row>
      <mx-table-cell>George</mx-table-cell>
      <mx-table-cell>Harrison</mx-table-cell>
      <mx-table-cell>2/25/1943</mx-table-cell>
      <mx-table-cell>
        <mx-icon-button el-aria-label="Like" icon="ph ph-heart" @click="clickHandler({ firstName: 'George' })"></mx-icon-button>
        <mx-icon-button el-aria-label="Delete" icon="ph ph-trash" @click="clickHandler({ firstName: 'George' })"></mx-icon-button>
      </mx-table-cell>
    </mx-table-row>
    <mx-table-row>
      <mx-table-cell>Ringo</mx-table-cell>
      <mx-table-cell>Starr</mx-table-cell>
      <mx-table-cell>7/7/1940</mx-table-cell>
      <mx-table-cell>
        <mx-icon-button el-aria-label="Like" icon="ph ph-heart" @click="clickHandler({ firstName: 'Ringo' })"></mx-icon-button>
        <mx-icon-button el-aria-label="Delete" icon="ph ph-trash" @click="clickHandler({ firstName: 'Ringo' })"></mx-icon-button>
      </mx-table-cell>
    </mx-table-row>
  </div>
</mx-table>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# Search & filter slots

The mx-table component has both a search slot to accomodate a Search field, and a filter slot for any additional filter components.

All Labels Apple Capitol Lingasong Parlophone Polydor
<mx-table
  rows-per-page="5"
  :rows-per-page-options.prop="[5, 10, 25, 50]"
  :rows.prop="filteredAlbums"
  :columns.prop="[
    { property: 'entertainer', heading: 'Artist', sortable: false },
    { property: 'album', heading: 'Album' },
    { property: 'releasedate', heading: 'Release Date', type: 'date' },
    { property: 'label', heading: 'Label' }
  ]"
>
  <mx-search
    slot="search"
    :value="albumSearch"
    dense
    placeholder="Search"
    @input="albumSearch = $event.target.value"
  />
  <div slot="filter">
    <mx-button ref="labelMenuButton" btn-type="simple" dropdown>
      {{ (this.albumLabelFilters.length || 'All') +
      (this.albumLabelFilters.length === 1 ? ' Label' : ' Labels') }}
    </mx-button>
    <mx-menu ref="labelMenu">
      <mx-menu-item
        v-for="label in albumLabels"
        :key="label"
        multi-select
        :checked="albumLabelFilters.includes(label)"
        @input="toggleLabelFilter(label)"
      >
        {{ label }}
      </mx-menu-item>
    </mx-menu>
  </div>
</mx-table>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
albumLabels() {
  return [...new Set(this.albums.map(album => album.label))].sort()
},
filteredAlbums() {
  if (!this.albumSearch && !this.albumLabelFilters.length) return this.albums
  let filteredAlbums = this.albums
  if (this.albumSearch) {
    const albumSearch = this.albumSearch.toLocaleLowerCase()
    filteredAlbums = filteredAlbums.filter(row => 
      row.album.toLocaleLowerCase().includes(albumSearch)
    )
  }
  if (this.albumLabelFilters.length) {
    filteredAlbums = filteredAlbums.filter(row => 
      this.albumLabelFilters.includes(row.label)
    )
  }
  return filteredAlbums
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# Empty state

The mx-table component has a default empty state for when there are no visible rows, which can be overridden using the empty-state slot.

Your search returned 0 results.

<mx-table
  :rows.prop="[]"
  :columns.prop="[
    { property: 'firstName', heading: 'First Name' },
    { property: 'lastName', heading: 'Last Name' },
    { property: 'credits', heading: 'Song Credits', type: 'number' },
  ]"
/>
<mx-table
  paginate="false"
  :rows.prop="[]"
  :columns.prop="[
    { property: 'firstName', heading: 'First Name' },
    { property: 'lastName', heading: 'Last Name' },
    { property: 'credits', heading: 'Song Credits', type: 'number' },
  ]"
>
  <p slot="empty-state" class="text-center opacity-50 text-h5 my-0">
    Your search returned 0 results.
  </p>
</mx-table>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Last updated 6/7/2024, 8:14:29 PM
<mx-table
  paginate="false"
  :rows.prop="beatles"
  :columns.prop="[
    { property: 'firstName', heading: 'First Name' },
    { property: 'lastName', heading: 'Last Name' },
    { property: 'credits', heading: 'Song Credits', type: 'number' },
    { property: 'birthdate', heading: 'Birthdate', type: 'date' },
    { property: 'eyeColor', heading: 'Eye Color', sortable: false }
  ]"
>
  <span slot="footer" class="italic">Last updated {{ new Date().toLocaleString() }}</span>
</mx-table>
1
2
3
4
5
6
7
8
9
10
11
12

# Server-side pagination

This example uses paginated data from An API of Ice And Fire (opens new window). To prevent client-side pagination, the server-paginate prop must be passed, and the pagination component's mxPageChange event should be leveraged to update the request parameters. If the API request needs to include sorting parameters as well, attach a listener to the mxSortChange event as well.

Other props that may be helpful when using server-side pagination include showProgressBar, progressAppearDelay, totalRows, disablePagination, and disableNextPage.

<mx-table
  server-paginate
  :page="this.apiPage"
  :rows-per-page="this.apiPageSize"
  :rows-per-page-options.prop="[5, 10, 25, 50]"
  :disable-pagination="apiLoading"
  :disable-next-page="apiDisableNextPage"
  :rows.prop="apiHouses"
  :columns.prop="[
    { property: 'name', heading: 'Name', sortable: false },
    { property: 'region', heading: 'Region', sortable: false },
    { property: 'words', heading: 'Words', sortable: false }
  ]"
  :show-progress-bar="apiLoading"
  progress-appear-delay="150"
  @mxPageChange="onPageChange"
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mounted() {
  this.getApiData()
  // ...
1
2
async getApiData() {
  this.apiLoading = true
  let url = 'https://www.anapioficeandfire.com/api/houses?'
  url += 'page=' + this.apiPage
  url += '&pageSize=' + this.apiPageSize
  const response = await fetch(url)
  // Parse last page number from "link" header since API does not give us total row count
  const pages = response.headers.get('link').match(/page\=[0-9]+/g)
  const lastPage = +/[0-9]+/.exec(pages[pages.length - 1])[0]
  // Disable next-page button if this is the last page
  this.apiDisableNextPage = lastPage === this.apiPage
  setTimeout(async () => {
    this.apiHouses = await response.json()
    this.apiLoading = false
  }, this.apiSlowRequest ? 1500 : 0)
},
onPageChange(e) {
  this.apiPage = e.detail.page
  this.apiPageSize = e.detail.rowsPerPage
  this.getApiData()
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# Draggable rows

Set the draggableRows prop to allow reordering rows via drag and drop.

<mx-table
  draggable-rows
  paginate="false"
  :rows.prop="draggableBeatles"
  :columns.prop="[
    { property: 'firstName', heading: 'First Name' },
    { property: 'lastName', heading: 'Last Name' },
    { property: 'credits', heading: 'Song Credits', type: 'number' },
    { property: 'birthdate', heading: 'Birthdate', type: 'date' },
  ]"
/>
1
2
3
4
5
6
7
8
9
10

By default, the rows array is mutated when reordering, so the reordered array may be read from HTMLMxTableElement.rows.

To disable this behavior, set mutateOnDrag to false. The component emits an mxRowMove event containing the rowId (if set), oldIndex, and newIndex for the dragged row. This information can then be used to update state in the host application. If a rows array was not provided, the oldIndex and newIndex are based on the row element's position relative to its siblings. See nested rows and grouped rows for other examples.

<mx-table
  draggable-rows
  checkable
  paginate="false"
  :get-row-id.prop="row => row.firstName"
  :rows.prop="draggableBeatles"
  :columns.prop="[
    { property: 'firstName', heading: 'First Name' },
    { property: 'lastName', heading: 'Last Name' },
    { property: 'credits', heading: 'Song Credits', type: 'number' },
    { property: 'birthdate', heading: 'Birthdate', type: 'date' },
  ]"
  mutate-on-drag="false"
  @mxRowMove="onRowMove"
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
onRowMove(e) {
  const { oldIndex, newIndex } = e.detail
  const beatles = this.draggableBeatles.slice()
  const row = beatles.splice(oldIndex, 1)[0]
  beatles.splice(newIndex, 0, row)
  this.draggableBeatles = beatles
},
1
2
3
4
5
6

# Nested rows

When using mx-table-row elements within the table's default slot, it is possible to nest table rows.

The example below combines nested rows with the draggableRows prop. For the sake of simplicity, the model in this example is not actually updated when a row is dropped. It is a good idea to provide a rowId when nesting draggable rows; the rowId will be emitted via the mxRowMove event, as well as the oldIndex and newIndex (relative to its siblings).

Chair $65.00 1 $65.00 Seat Cushion $9.00 1 $9.00 Produce - - $24.94 Roma Tomato (lb) $0.99 12 $11.88 Avocado, Large $1.79 4 $7.16 Cucumber - - $5.90 English Cucumber $2.59 10 $25.90 Apron $16.00 4 $64.00
<mx-table
  draggable-rows
  :columns.prop="[
    { heading: 'Item', sortable: false },
    { heading: 'Cost', sortable: false, align: 'right' },
    { heading: 'Qty', sortable: false, align: 'right'  },
    { heading: 'Total Cost', sortable: false, align: 'right' }
  ]"
  @mxRowMove="onNestedRowMove"
>
  <div>
    <mx-table-row row-id="0">
      <mx-table-cell>Chair</mx-table-cell>
      <mx-table-cell>$65.00</mx-table-cell>
      <mx-table-cell>1</mx-table-cell>
      <mx-table-cell>$65.00</mx-table-cell>
      <mx-table-row row-id="7">
        <mx-table-cell>Seat Cushion</mx-table-cell>
        <mx-table-cell>$9.00</mx-table-cell>
        <mx-table-cell>1</mx-table-cell>
        <mx-table-cell>$9.00</mx-table-cell>
      </mx-table-row>
    </mx-table-row>
    <mx-table-row row-id="1">
      <mx-table-cell>Produce</mx-table-cell>
      <mx-table-cell>-</mx-table-cell>
      <mx-table-cell>-</mx-table-cell>
      <mx-table-cell>$24.94</mx-table-cell>
      <mx-table-row row-id="2">
        <mx-table-cell>Roma Tomato (lb)</mx-table-cell>
        <mx-table-cell>$0.99</mx-table-cell>
        <mx-table-cell>12</mx-table-cell>
        <mx-table-cell>$11.88</mx-table-cell>
      </mx-table-row>
      <mx-table-row row-id="3">
        <mx-table-cell>Avocado, Large</mx-table-cell>
        <mx-table-cell>$1.79</mx-table-cell>
        <mx-table-cell>4</mx-table-cell>
        <mx-table-cell>$7.16</mx-table-cell>
      </mx-table-row>
      <mx-table-row row-id="4">
        <mx-table-cell>Cucumber</mx-table-cell>
        <mx-table-cell>-</mx-table-cell>
        <mx-table-cell>-</mx-table-cell>
        <mx-table-cell>$5.90</mx-table-cell>
        <mx-table-row row-id="5" do-not-drag>
          <mx-table-cell>English Cucumber</mx-table-cell>
          <mx-table-cell>$2.59</mx-table-cell>
          <mx-table-cell>10</mx-table-cell>
          <mx-table-cell>$25.90</mx-table-cell>
        </mx-table-row>
      </mx-table-row>
    </mx-table-row>
    <mx-table-row row-id="6">
      <mx-table-cell>Apron</mx-table-cell>
      <mx-table-cell>$16.00</mx-table-cell>
      <mx-table-cell>4</mx-table-cell>
      <mx-table-cell>$64.00</mx-table-cell>
    </mx-table-row>
  </div>
</mx-table>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
onNestedRowMove(e) {
  const { rowId, oldIndex, newIndex } = e.detail
  const upOrDown = oldIndex < newIndex ? 'down' : 'up'
  const distance = Math.abs(newIndex - oldIndex)
  console.log(`Row with id ${rowId} was moved ${upOrDown} ${distance} position(s).`)
},
1
2
3
4
5

Nested rows can be collapsed under the parent row by toggling the collapseNestedRows prop. If there are rows that should never be collapsed, set the doNotCollapse prop on those rows.

Click the icon in the example below to expand the collapsed rows.

Home Home page Products All products Appliances Refrigerators, dishwashers, ovens Flooring Carpet, hardwood, tile Sale Items Clearance, Black Friday Contact Contact and support information
<mx-table
  :columns.prop="[
    { heading: 'Name', sortable: false },
    { heading: 'Description', sortable: false },
  ]"
>
  <div>
    <mx-table-row>
      <mx-table-cell>Home</mx-table-cell>
      <mx-table-cell>Home page</mx-table-cell>
    </mx-table-row>
    <mx-table-row collapse-nested-rows>
      <mx-table-cell>
        <i class="ph ph-plus py-4 pr-8 cursor-pointer" @click.stop="toggleNestedRows($event)" />
        Products
      </mx-table-cell>
      <mx-table-cell>All products</mx-table-cell>
      <mx-table-row>
        <mx-table-cell>Appliances</mx-table-cell>
        <mx-table-cell>Refrigerators, dishwashers, ovens</mx-table-cell>
      </mx-table-row>
      <mx-table-row>
        <mx-table-cell>Flooring</mx-table-cell>
        <mx-table-cell>Carpet, hardwood, tile</mx-table-cell>
      </mx-table-row>
      <mx-table-row do-not-collapse>
        <mx-table-cell>Sale Items</mx-table-cell>
        <mx-table-cell>Clearance, Black Friday</mx-table-cell>
      </mx-table-row>
    </mx-table-row>
    <mx-table-row>
      <mx-table-cell>Contact</mx-table-cell>
      <mx-table-cell>Contact and support information</mx-table-cell>
    </mx-table-row>
  </div>
</mx-table>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
toggleNestedRows(e) {
  const row = e.target.closest('mx-table-row')
  row.collapseNestedRows = !row.collapseNestedRows
  e.target.closest('i').classList.toggle('ph-plus')
  e.target.closest('i').classList.toggle('ph-minus')
}
1
2
3
4
5

# Grouping and subheader rows

The groupBy prop specifies a property on the rows objects to use for grouping rows under subheaders.

If the groupBy values are not suitable for use as headings, pass a getGroupByHeading function to translate them into display-friendly headings for the subheader rows.

[ { "name": "Matrix", "section": "Core Tools" }, { "name": "Remine", "section": "Core Tools" }, { "name": "Realist", "section": "Core Tools" }, { "name": "Builders Update", "section": "Additional Benefits" }, { "name": "ePropertyWatch", "section": "Additional Benefits" }, { "name": "Homes.com", "section": "Additional Benefits" }, { "name": "FarmersOnly.com", "section": "Dating" } ]
<mx-table
  ref="appsTable"
  auto-width
  draggable-rows
  paginate="false" 
  :rows.prop="apps"
  :columns.prop="[{ property: 'name', heading: 'Name', sortable: false }]"
  group-by="section"
  @mxRowMove="() => apps = $refs.appsTable.rows"
/>
<code class="whitespace-pre-wrap">{{ apps }}</code>
1
2
3
4
5
6
7
8
9
10

Adding the subheader prop to a row styles it as a subheader. Only one mx-table-cell is necessary for the subheader content. When used inside a table with draggable rows, it can be used to drag a group of nested rows.

Core Tools Matrix Remine Realist Additional Benefits Builders Update ePropertyWatch Homes.com
<mx-table
  auto-width
  paginate="false"
  :columns.prop="[
    { heading: 'Name' },
  ]"
>
  <div>
    <mx-table-row subheader>
      <mx-table-cell>Core Tools</mx-table-cell>
      <mx-table-row>
        <mx-table-cell>Matrix</mx-table-cell>
      </mx-table-row>
      <mx-table-row>
        <mx-table-cell>Remine</mx-table-cell>
      </mx-table-row>
      <mx-table-row>
        <mx-table-cell>Realist</mx-table-cell>
      </mx-table-row>
    </mx-table-row subheader>
    <mx-table-row subheader>
      <mx-table-cell>Additional Benefits</mx-table-cell>
      <mx-table-row>
        <mx-table-cell>Builders Update</mx-table-cell>
      </mx-table-row>
      <mx-table-row>
        <mx-table-cell>ePropertyWatch</mx-table-cell>
      </mx-table-row>
      <mx-table-row>
        <mx-table-cell>Homes.com</mx-table-cell>
      </mx-table-row>
    </mx-table-row subheader>
  </div>
</mx-table>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# Advanced usage

The following example combines checkable, slotted table rows with pagination, row actions, multi-row actions, searching, and filtering. It also uses an alternate mobile layout for the operations bar where the search slot is on the first row and the filter slot is on the second row (via the mobileSearchOnTop prop).

All Labels Apple Capitol Lingasong Parlophone Polydor
The Beatles Please Please Me 3/22/1963 ParlophoneThe Beatles With The Beatles 11/22/1963 ParlophoneThe Beatles A Hard Day's Night 7/10/1964 ParlophoneThe Beatles Beatles for Sale 12/4/1964 ParlophoneThe Beatles Help! 8/6/1965 ParlophoneThe Beatles Rubber Soul 12/3/1965 ParlophoneThe Beatles Revolver 8/5/1966 ParlophoneThe Beatles Sgt. Pepper's Lonely Hearts Club Band 6/1/1967 ParlophoneThe Beatles Magical Mystery Tour 11/27/1967 ParlophoneThe Beatles The Beatles (The White Album) 11/22/1968 AppleThe Beatles Yellow Submarine 1/13/1969 AppleThe Beatles Abbey Road 9/26/1969 AppleThe Beatles Let It Be 5/8/1970 AppleThe Beatles 1962-1966 (Red Album) 4/19/1973 AppleThe Beatles 1967-1970 (Blue Album) 4/19/1973 AppleThe Beatles Rock 'n' Roll Music 6/7/1976 ParlophoneThe Beatles Live! at the Star-Club in Hamburg, Germany; 1962 4/8/1977 LingasongThe Beatles Love Songs 11/19/1977 ParlophoneThe Beatles Rarities 12/2/1978 ParlophoneThe Beatles The Beatles at the Hollywood Bowl 5/4/1977 ParlophoneThe Beatles The Beatles' Christmas Album 12/18/1970 AppleThe Beatles Past Masters, Volume One 3/7/1988 ParlophoneThe Beatles Past Masters, Volume Two 3/7/1988 ParlophoneThe Beatles Anthology 1 11/20/1995 AppleThe Beatles Anthology 2 3/18/1996 AppleThe Beatles Anthology 3 10/28/1996 AppleThe Beatles 1 11/13/2000 AppleThe Beatles Let It Be... Naked 11/17/2003 AppleThe Beatles Love 11/20/2006 AppleThe Beatles On Air – Live at the BBC Volume 2 11/11/2013 AppleThe Beatles The Beatles' First 4/10/1964 PolydorThe Beatles The U.S. Albums 1/21/2014 AppleThe Beatles The Beatles in Mono 9/9/2009 AppleThe Beatles Live at the BBC 11/30/1994 AppleThe Beatles The Beatles Stereo Box Set 9/9/2009 AppleThe Beatles The Capitol Albums, Volume 1 11/16/2004 CapitolThe Beatles The Capitol Albums, Volume 2 4/11/2006 CapitolThe Beatles Yellow Submarine Songtrack 9/13/1999 AppleThe Beatles Tomorrow Never Knows 7/24/2012 AppleThe Beatles The Beatles Bootleg Recordings 1963 12/17/2013 AppleThe Beatles The Singles Collection 11/22/2019 AppleThe Beatles Abbey Road Anniversary Edition 9/27/2019 AppleThe Beatles Beatles VI 6/14/1965 CapitolThe Beatles Yesterday and Today 6/15/1966 CapitolThe Beatles Hey Jude 2/26/1970 AppleThe Beatles The Beatles' Hits EP 9/6/1963 Parlophone
<mx-table
  checkable
  mobile-search-on-top
  :rows.prop="filteredAlbums2"
  :columns.prop="[
    { property: 'entertainer', heading: 'Artist', sortable: false },
    { property: 'album', heading: 'Album' },
    { property: 'releasedate', heading: 'Release Date', type: 'date' },
    { property: 'label', heading: 'Label' }
  ]"
  :get-multi-row-actions.prop="rowIds => ([
    {
      value: 'Like',
      icon: 'ph ph-heart',
      onClick: () => multiRowClickHandler(rowIds)
    },
    {
      value: 'Delete',
      icon: 'ph ph-trash',
      onClick: () => multiRowClickHandler(rowIds)
    }
  ])"
  @mxVisibleRowsChange="e => albumRows = e.detail"
>
  <mx-search
    slot="search"
    :value="albumSearch2"
    dense
    placeholder="Search"
    @input="albumSearch2 = $event.target.value"
  />
  <div slot="filter">
    <mx-button ref="labelMenuButton2" class="whitespace-nowrap" btn-type="simple" dropdown>
      {{ (this.albumLabelFilters2.length || 'All') +
      (this.albumLabelFilters2.length === 1 ? ' Label' : ' Labels') }}
    </mx-button>
    <mx-menu ref="labelMenu2">
      <mx-menu-item
        v-for="label in albumLabels"
        :key="label"
        multi-select
        :checked="albumLabelFilters2.includes(label)"
        @input="toggleLabelFilter2(label)"
      >
        {{ label }}
      </mx-menu-item>
    </mx-menu>
  </div>
  <div>
    <mx-table-row
      v-for="(row, i) in albumRows"
      :key="row.album"
      :row-id="row.album"
      :actions.prop="[
        { value: 'Like', icon: 'ph ph-heart', onClick: () => clickHandler(row) },
        { value: 'Delete', icon: 'ph ph-trash', onClick: () => clickHandler(row) },
      ]"
    >
      <mx-table-cell>{{ row.entertainer }}</mx-table-cell>
      <mx-table-cell>{{ row.album }}</mx-table-cell>
      <mx-table-cell>{{ new Date(row.releasedate).toLocaleDateString() }}</mx-table-cell>
      <mx-table-cell>{{ row.label }}</mx-table-cell>
    </mx-table-row>
  </div>
</mx-table>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

# mx-table

# Properties

Property Attribute Description Type Default
autoWidth auto-width Set to true to disable scrolling and allow smaller tables to shrink to less than 100% width on larger screens boolean false
checkOnRowClick check-on-row-click Set to true to allow checking rows by clicking on any dead space inside the row. boolean false
checkable checkable Make rows checkable. You must either provide a getRowId getter (for generated rows), or provide a rowId for every mx-table-row if creating the rows manually in the table's slot. boolean false
columns -- An array of ITableColumn column definitions. If not specified, a column will be generated for each property on the row object. ITableColumn[] []
disableNextPage disable-next-page Disable the next-page button. Useful when using server-side pagination and the total number of rows is unknown. boolean false
disablePagination disable-pagination Disable the pagination buttons (i.e. while loading results) boolean false
draggableRows draggable-rows Enables reordering of rows via drag and drop. boolean false
getGroupByHeading -- A function that returns the subheader text for a groupBy value. If not provided, the row[groupBy] value will be shown in the subheader rows. (row: unknown) => string undefined
getMultiRowActions -- (rows: string[]) => ITableRowAction[] undefined
getRowActions -- (row: unknown) => ITableRowAction[] undefined
getRowId -- A function that returns the rowId prop for each generated mx-table-row. This is only required if the table is checkable and is auto-generating rows (not using the default slot). (row: unknown) => string undefined
groupBy group-by The row property to use for grouping rows. The rows prop must be provided as well. string null
hoverable hoverable boolean true
mobileSearchOnTop mobile-search-on-top Set to true to use an alternate mobile layout for the operations bar where the filter slot is next to the (un)check-all checkbox and the search slot is in a row above. boolean false
mutateOnDrag mutate-on-drag Set to false to not mutate the rows prop when rows are reordered via drag and drop. boolean true
operationsBarClass operations-bar-class Additional class names for the operation bar grid string ''
page page The page to display number 1
paginate paginate Show the pagination component. Setting this to false will show all rows. boolean true
progressAppearDelay progress-appear-delay Delay the appearance of the progress bar for this many milliseconds number 0
progressValue progress-value The progress bar percentage from 0 to 100. If not provided (or set to null), an indeterminate progress bar will be displayed. number null
rows -- An array of objects that defines the table's dataset. unknown[] []
rowsPerPage rows-per-page number 10
rowsPerPageOptions -- number[] undefined
serverPaginate server-paginate Do not sort or paginate client-side. Use events to send server requests instead. boolean false
showCheckAll show-check-all Set to false to hide the (un)check all checkbox at the top of the table. boolean true
showProgressBar show-progress-bar Show a progress bar below the header row boolean false
sortAscending sort-ascending boolean true
sortBy sort-by The property on the row objects that will be used for sorting string undefined
totalRows total-rows The total number of unpaginated rows. This is ignored for client-side pagination. For server-side pagination, omitting this prop will remove the page number and last-page buttons. number undefined

# Events

Event Description Type
mxCheckAll Emitted when the (un)check-all checkbox is clicked. The Event.detail will be the new checked value. CustomEvent<boolean>
mxRowCheck Emitted when a row is (un)checked. The Event.detail will be the array of checked rowIds. CustomEvent<string[]>
mxRowMove Emitted when a row is dragged to a new position. The Event.detail object will contain the rowId (if set), oldIndex, and newIndex. CustomEvent<any>
mxSortChange Emitted when a sortable column's header is clicked. CustomEvent<{ sortBy: string; sortAscending: boolean; }>
mxVisibleRowsChange Emitted when the sorting, pagination, or rows data changes. The Event.detail will contain the sorted, paginated array of visible rows. This is useful for building a custom row layout via the default slot. CustomEvent<unknown[]>

# Methods

# checkAll() => Promise<void>

# Returns

Type: Promise<void>

# checkNone() => Promise<void>

# Returns

Type: Promise<void>

# getCheckedRowIds() => Promise<string[]>

# Returns

Type: Promise<string[]>

# setCheckedRowIds(checkedRowIds?: string[]) => Promise<void>

# Returns

Type: Promise<void>

# Dependencies

# Depends on

# Graph

graph TD;
  mx-table --> mx-table-row
  mx-table --> mx-table-cell
  mx-table --> mx-checkbox
  mx-table --> mx-button
  mx-table --> mx-menu
  mx-table --> mx-menu-item
  mx-table --> mx-linear-progress
  mx-table --> mx-pagination
  mx-table-row --> mx-checkbox
  mx-table-row --> mx-button
  mx-table-row --> mx-icon-button
  mx-table-row --> mx-menu
  mx-table-row --> mx-menu-item
  mx-menu-item --> mx-checkbox
  mx-pagination --> mx-menu
  mx-pagination --> mx-menu-item
  style mx-table fill:#f9f,stroke:#333,stroke-width:4px
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

If you are having trouble setting a prop or adding an event listener, refer to this page.

# ITableColumn

The ITableColumn interface describes the objects passed to the columns prop.

Property Description Type Default
align 'left' \| 'center' \| 'right' left for strings, right for numbers
cellClass Additional classes to add to the body cells in this column string undefined
getValue A getter function for the column cells' inner HTML. Note that a property is required to make a column with a value getter sortable. If sorting client-side, the property does not necessarily have to exist on the row objects; it is simply a unique identifier for the table's sortBy prop. (row: unknown, rowIndex?: number) => any undefined
headerClass Additional classes to add to the header cell for this column string undefined
heading The displayed column heading string undefined
isActionColumn If set to true, this column will be given a screenreader-only heading of "Action" boolean false
property The property on each row object that will supply the column's cell values (as HTML). You may also supply a getValue function for the value. If both are provided, the property will only be used for sorting. string undefined
sortable Whether the column may be sorted by clicking the header. The column must specify a property to be sortable. boolean true
sortCompare A custom compare function for sorting by this column (if sorting client-side) (rowA: unknown, rowB: unknown) => number undefined
type The value type, which may affect sorting and how the value is displayed 'string' \| 'number' \| 'date' \| 'dateTime' \| 'boolean' string

# mx-table-row

# Properties

Property Attribute Description Type Default
actions -- An array of Menu Item props to create the actions menu, including a value property for each menu item's inner text. ITableRowAction[] []
checked checked boolean false
collapseNestedRows collapse-nested-rows Toggles the visibility of all nested rows (except those set to doNotCollapse) boolean false
doNotCollapse do-not-collapse Do not collapse this row if the parent row's collapseNestedRows prop is set to true. boolean false
doNotDrag do-not-drag Do not allow dragging of this row even if the parent table's draggableRows prop is set to true. boolean false
rowId row-id This is required for checkable rows in order to persist the checked state through sorting and pagination. string undefined
rowIndex row-index This row's index in the HTMLMxTableElement.rows array. This is set internally by the table component. number undefined
subheader subheader Style the row as a subheader. boolean false

# Events

Event Description Type
mxCheck Emits the rowId and checked state (via Event.detail) of the row whenever it is (un)checked CustomEvent<{ rowId: string; checked: boolean; }>
mxDragKeyDown Emits the KeyboardEvent.key when a key is pressed while keyboard dragging. Handled by the parent table. CustomEvent<string>
mxRowAccordion Emitted when a row is collapsed or expanded. Handled by the parent table. CustomEvent<void>
mxRowDragEnd Emitted when dragging ends. Handled by the parent table. CustomEvent<{ isKeyboard: boolean; isCancel: boolean; }>
mxRowDragStart Emitted when dragging starts. Handled by the parent table. CustomEvent<{ isKeyboard: boolean; }>

# Methods

# focusDragHandle() => Promise<void>

# Returns

Type: Promise<void>

# getChildren() => Promise<HTMLElement[]>

Returns the immediate children of the row, as well as the immediate children of all nested rows. If a child is display: contents (i.e. the first column wrapper), then its children are added.

# Returns

Type: Promise<HTMLElement[]>

# getHeight() => Promise<number>

Calculate the height of the row, including the height of nested rows

# Returns

Type: Promise<number>

# getNestedRowIndexes() => Promise<number[]>

Get an array of row IDs for rows nested directly inside this row

# Returns

Type: Promise<number[]>

# toggle(hideRow: boolean, skipTransition: boolean) => Promise<void>

Show/hide the row (with an optional accordion transition)

# Returns

Type: Promise<void>

# translateRow(x: number, y: number) => Promise<void>

Apply a CSS transform to translate the row by x and y pixels

# Returns

Type: Promise<void>

# Dependencies

# Used by

# Depends on

# Graph

graph TD;
  mx-table-row --> mx-checkbox
  mx-table-row --> mx-button
  mx-table-row --> mx-icon-button
  mx-table-row --> mx-menu
  mx-table-row --> mx-menu-item
  mx-menu-item --> mx-checkbox
  mx-table --> mx-table-row
  style mx-table-row fill:#f9f,stroke:#333,stroke-width:4px
1
2
3
4
5
6
7
8
9

If you are having trouble setting a prop or adding an event listener, refer to this page.