# Custom Template

{% hint style="warning" %}
when using this advanced feature please take extra care not to break any existing functionality (e.g do not remove events or essential attributes like `value` or `ref`)&#x20;
{% endhint %}

As of version 2 the package offers the ability to seamlessly swap any table component with your own UI implementation.

## Usage

Swapped components are declared as the fifth argument you pass when registering the component:

```javascript
Vue.use(ClientTable, [options = {}], [useVuex = false], [theme = 'bootstrap3'], {
    [componentAKey]: ComponentA,
    [componentBKey]: ComponentB,
    //etc...
});
```

Each component has its current template stored in the Github repo under the [templates](https://github.com/matfish2/vue-tables-2/tree/master/templates) folder. All components have a single `props` prop, which is an object containing all contextual data needed to build the component. More on this in the examples section. The full structure of the components tree, along with the corresponding keys is listed below:

* [VtDataTable](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtDataTable.vue): dataTable
  * [VtGenericFilter](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtGenericFilter.vue): genericFilter
  * [VtPerPageSelector](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtPerPageSelector.vue): perPageSelector
  * [VtDropdownPagination](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtDropdownPagination.vue): dropdownPagination
  * [VtColumnsDropdown](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtColumnsDropdown.vue): columnsDropdown
  * [VtTable](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtTable.vue): table
    * [VtTableHead](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtTableHead.vue): tableHead
      * [VtHeadingsRow](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtHeadingsRow.vue): headingsRow
        * [VtTableHeading](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtTableHeading.vue): tableHeading
          * [VtSortControl](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtSortControl.vue): sortControl
        * [VtFiltersRow](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtFiltersRow.vue): filtersRow
          * [VtTextFilter](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtTextFilter.vue): textFilter
          * [VtListFilter](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtListFilter.vue): listFilter
          * [VtDateFilter](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtDateFilter.vue): dateFilter
    * [VtTableBody](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtTableBody.vue): tableBody
      * [VtNoResultsRow](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtNoResultsRow.vue): noResultsRow
      * [VtTableRow](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtTableRow.vue): tableRow
        * [VtTableCell](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtTableCell.vue): tableCell
        * [VtChildRowToggler](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtChildRowToggler.vue): childRowToggler
      * [VtChildRow](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtChildRow.vue): childRow
      * [VtGroupRow](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtGroupRow.vue): groupRow
  * [VtPagination](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtPagination.vue): pagination

{% hint style="info" %}
All components provide an `opts` property, referencing the full `options` object (i.e the merged default, global and local options)
{% endhint %}

{% hint style="warning" %}
Users of premium version, use the template folder of the private repository instead`(node_modules/vue-tables-2/templates),`as there might be some differences in some of the templates
{% endhint %}

## Examples

### Custom Pagination

Let's swap the default pagination, which uses [vue-pagination-2](https://www.npmjs.com/package/vue-pagination-2), with some other 3rd party implementaion, say [vue-plain-pagination](https://www.npmjs.com/package/vue-plain-pagination) .&#x20;

Copy the existing pagination template:

```markup
<template>
        <pagination
                :options="props.optionsObj"
                :for="props.name"
                :vuex="props.vuex"
                :records="props.records"
                :per-page="props.perPage"
                v-model="props.page"
                @paginate="page => props.setPage(page)"/>
</template>

<script>
    import Pagination from 'vue-pagination-2'

    export default {
        name: "VtPagination",
        components: {
            Pagination
        },
        props: ['props']
    }
</script>
```

Modify it like so, and save to your project:

**MyPagination.vue**

```markup
<template>
    <v-pagination v-model="props.page"
                  :page-count="props.totalPages"
                  @input="page => props.setPage(page)"/>
</template>

<script>
    import vPagination from 'vue-plain-pagination'

    export default {
        name: "VtPagination",
        components: {
            vPagination
        },
        props: ['props']
    }
</script>
```

Register it:

```javascript
Vue.use(ClientTable, {}, false, 'bootstrap3', {
    pagination: MyPagination
});
```

Et Voila!

{% hint style="info" %}
In this example we used the `totalPages` prop, which wasn't used in the original component. If you encounter a use-case for other props which are not exposed through the `props` object, let me know and I'll consider adding it to the provided properties&#x20;
{% endhint %}

### Font Awesome Sort Icon

Let's use [vue-fontawesome](https://www.npmjs.com/package/@fortawesome/vue-fontawesome)

Install the icons:

```javascript
import { library } from '@fortawesome/fontawesome-svg-core'
import { faSort, faSortUp, faSortDown } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'

library.add(faSort);
library.add(faSortUp);
library.add(faSortDown);
```

Copy the original template:

```markup
<template>
     <span v-if="props.sortable" :class="props.class">
     </span>
</template>

<script>
    export default {
        name: "VtSortControl",
        props: ['props']
    }
</script>
```

Modify and save to your project:

**MySortControl.vue**

```markup
<template>
    <font-awesome-icon v-if="props.sortable" :icon="icon" class="fa-pull-right"/>
</template>

<script>
    export default {
        name: "VtSortControl",
        props: ['props'],
        computed: {
            icon() {
                // sortStatus = { sorted: Boolean, asc: Boolean }
                
                // if not sorted return base icon
                if (!this.props.sortStatus.sorted) return 'sort';

                // return sort direction icon
                return this.props.sortStatus.asc ? 'sort-up' : 'sort-down';
            }
        }
    }
</script>
```

{% hint style="info" %}
As in the example above, note we are using the `sortStatus` property. which wasn't used in the original template
{% endhint %}

Register it:

```javascript
Vue.use(ClientTable, {}, false, 'bootstrap3', {
    sortControl: MySortControl
});
```

### Footer Pagination

In this example we will reposition the pagination element inside the table by moving it from `VtDataTable` to `VtTable`:

Remove pagination from `VtDataTable`:

**MyDataTable.vue**

```markup
<template>

    <div :class="`VueTables VueTables--${props.source}`" slot-scope="props">

        <div :class="props.theme.row">
            <div :class="props.theme.column">
                <div v-if="!props.opts.filterByColumn && props.opts.filterable"
                     :class="`${props.theme.field} ${props.theme.inline} ${props.theme.left} VueTables__search`">
                    <vnodes :vnodes="props.slots.beforeFilter"/>
                    <vt-generic-filter/>
                    <vnodes :vnodes="props.slots.afterFilter"/>
                </div>
                <vnodes :vnodes="props.slots.afterFilterWrapper"/>

                <div v-if="props.perPageValues.length > 1"
                     :class="`${props.theme.field} ${props.theme.inline} ${props.theme.right} VueTables__limit`">
                    <vnodes :vnodes="props.slots.beforeLimit"/>
                    <vt-per-page-selector/>
                    <vnodes :vnodes="props.slots.afterLimit"/>

                </div>

                <div class="VueTables__pagination-wrapper" v-if="props.opts.pagination.dropdown && props.totalPages > 1">
                    <div :class="`${props.theme.field} ${props.theme.inline} ${props.theme.right} VueTables__dropdown-pagination`">
                        <vt-dropdown-pagination/>
                    </div>
                </div>

                <div v-if="props.opts.columnsDropdown"
                     :class="`VueTables__columns-dropdown-wrapper ${props.theme.right} ${props.theme.dropdown.container}`">
                    <vt-columns-dropdown/>
                </div>
            </div>
        </div>

        <vnodes :vnodes="props.slots.beforeTable"/>
        <div class="table-responsive">
            <vt-table ref="vt_table"/>
        </div>
        <vnodes :vnodes="props.slots.afterTable"/>

    </div>
</template>

<script>
    import VtColumnsDropdown from 'vue-tables-2/compiled/components/VtColumnsDropdown'
    import VtDropdownPagination from 'vue-tables-2/compiled/components/VtDropdownPagination'
    import VtGenericFilter from 'vue-tables-2/compiled/components/VtGenericFilter'
    import VtPerPageSelector from 'vue-tables-2/compiled/components/VtPerPageSelector';
    import VtTable from 'vue-tables-2/compiled/components/VtTable';

    export default {
        name: "VtDataTable",
        props: ['props'],
        components: {
            VtGenericFilter,
            VtPerPageSelector,
            VtColumnsDropdown,
            VtDropdownPagination,
            VtTable,
            vnodes: {
                functional: true,
                render: (h, ctx) => ctx.props.vnodes
            }
        }
    }
</script>
```

Add pagination to `VtTable`:

**MyTable.vue**

```markup
<template>
    <table v-bind="props.tableAttrs">
        <caption v-if="props.caption">{{props.caption}}</caption>
        <vt-table-head/>
        <vnodes :vnodes="props.slots.beforeBody"/>
        <vt-table-body/>
        <vnodes :vnodes="props.slots.afterBody"/>
        <tfoot>
        <tr>
            <td :colspan="props.colspan">
                <vt-pagination/>
            </td>
        </tr>
        </tfoot>
    </table>

</template>

<script>
    import VtTableHead from 'vue-tables-2/compiled/components/VtTableHead'
    import VtTableBody from 'vue-tables-2/compiled/components/VtTableBody'
    import VtPagination from 'vue-tables-2/compiled/components/VtPagination'

    export default {
        name: "VtTable",
        props: ['props'],
        components: {
            VtTableHead,
            VtTableBody,
            VtPagination,
            vnodes: {
                functional: true,
                render: (h, ctx) => ctx.props.vnodes
            }
        }
    }
</script>
```

Register Components:

```javascript
Vue.use(ClientTable, {}, false,'bootstrap3', {
    dataTable: MyDataTable,
    table: MyTable
});
```

### Footer Headings

Add `vtHeadingsRow` to `vtTable`:

MyTable.vue

```javascript
<template>
    <table v-bind="props.tableAttrs">
        <caption v-if="props.caption">{{props.caption}}</caption>
        <vt-table-head/>
        <vnodes :vnodes="props.slots.beforeBody"/>
        <vt-table-body/>
        <vnodes :vnodes="props.slots.afterBody"/>
        <tfoot>
            <vt-headings-row/>
        </tfoot>
    </table>
</template>

<script>
    import VtTableHead from 'vue-tables-2/compiled/components/VtTableHead'
    import VtTableBody from 'vue-tables-2/compiled/components/VtTableBody'
    import VtHeadingsRow from 'vue-tables-2/compiled/components/VtHeadingsRow'

    export default {
        name: "MyTable",
        props: ['props'],
        components: {
            VtTableHead,
            VtTableBody,
            VtHeadingsRow,
            vnodes: {
                functional: true,
                render: (h, ctx) => ctx.props.vnodes
            }
        }
    }
</script>
```

Register the component:

```javascript
Vue.use(ClientTable, {}, false,'bootstrap3', {
    table: MyTable
});
```

### Numeric Field Pagination

Say you have a huge number of pages in your server table, which prevents you from using dropdown pagination due to memory limitations. Let's replace dropdown pagination ([VtDropdownPagination](https://github.com/matfish2/vue-tables-2/blob/modular/templates/VtDropdownPagination.vue)) with a numeric field.

**NumericFieldPagination.vue**

```markup
<template>
    <input class="form-control"
           type="number"
           name="page"
           ref="page"
           :value="props.page"
           @input="setPage"/>
</template>

<script>

    import { debounce } from 'debounce'

    export default {
        name: "VtDropdownPagination",
        props: ['props'],
        methods: {
            setPage: debounce(function (e) {
                var page = e.target.value;

                if (page >= 1 && page <= this.props.totalPages) {
                    this.props.setPage(page);
                }
            }, 250)
        }
    }
</script>
```

Register the component:

```javascript
Vue.use(ServerTable, {}, false,'bootstrap3', {
    dropdownPagination: NumericFieldPagination
});
```

### Select2 List Filter

Replace [VtListFilter](https://github.com/matfish2/vue-tables-2/blob/master/templates/VtListFilter.vue) with the following:

```markup
<template>
    <div class="VueTables__list-filter"
         :id="`VueTables__${props.column}-filter`">
    <select :class="props.theme.select"
            :name="props.name"
            :value="props.value">
        <option value="">
            {{props.defaultOption}}
        </option>
        <option v-for="option in props.items" :value="option.id">
            {{option.text}}
        </option>
    </select>
    </div>
</template>

<script>
    export default {
        name: "MyListFilter",
        props: ['props'],
        mounted() {
            $(this.$el).find('select').select2()
                .on('select2:select', (e) => {
               this.props.query[this.props.column] =  e.target.value
               this.props.search(false)
            });
        }
    }
</script>
```

Register the component:

```javascript
Vue.use(ServerTable, {}, false,'bootstrap3', {
    listFilter: MyListFilter
});
```

### Vuetify

{% hint style="warning" %}
This is NOT a complete example. It's what I used recently for a project I was working on, and can serve as a solid starting point for further modifications.&#x20;
{% endhint %}

VtDataTable:

```markup
<template>
    <v-container fluid :class="`VueTables VueTables--${props.source}`" slot-scope="props">
        <v-row>
            <v-col cols="12" :class="props.theme.column">
                <div v-if="!props.opts.filterByColumn && props.opts.filterable"
                     :class="`${props.theme.field} ${props.theme.inline} ${props.theme.left} VueTables__search`">
                    <vnodes :vnodes="props.slots.beforeFilter"/>
                    <vt-generic-filter/>
                    <vnodes :vnodes="props.slots.afterFilter"/>
                </div>
                <vnodes :vnodes="props.slots.afterFilterWrapper"/>

                <div v-if="props.perPageValues.length > 1"
                     :class="`${props.theme.field} ${props.theme.inline} ${props.theme.right} VueTables__limit`">
                    <vnodes :vnodes="props.slots.beforeLimit"/>
                    <vt-per-page-selector/>
                    <vnodes :vnodes="props.slots.afterLimit"/>

                </div>

                <div class="VueTables__pagination-wrapper" v-if="props.opts.pagination.dropdown && props.totalPages > 1">
                    <div :class="`${props.theme.field} ${props.theme.inline} ${props.theme.right} VueTables__dropdown-pagination`">
                        <vt-dropdown-pagination/>
                    </div>
                </div>

                <div v-if="props.opts.columnsDropdown"
                     :class="`VueTables__columns-dropdown-wrapper ${props.theme.right} ${props.theme.dropdown.container}`">
                    <vt-columns-dropdown/>
                </div>
            </v-col>
        </v-row>

        <vnodes :vnodes="props.slots.beforeTable"/>
        <div class="table-responsive">
            <vt-table ref="vt_table"/>
        </div>
        <vnodes :vnodes="props.slots.afterTable"/>

        <vt-pagination/>

    </v-container>
</template>

<script>
    import VtColumnsDropdown from 'vue-tables-2/compiled/components/VtColumnsDropdown'
    import VtDropdownPagination from 'vue-tables-2/compiled/components/VtDropdownPagination'
    import VtGenericFilter from 'vue-tables-2/compiled/components/VtGenericFilter'
    import VtPerPageSelector from 'vue-tables-2/compiled/components/VtPerPageSelector';
    import VtPagination from 'vue-tables-2/compiled/components/VtPagination'
    import VtTable from 'vue-tables-2/compiled/components/VtTable';

    export default {
        name: "VtDataTable",
        props: ['props'],
        components: {
            VtGenericFilter,
            VtPerPageSelector,
            VtColumnsDropdown,
            VtDropdownPagination,
            VtTable,
            VtPagination,
            vnodes: {
                functional: true,
                render: (h, ctx) => ctx.props.vnodes
            }
        }
    }
</script>
```

VtTable:

```markup
<template>
    <v-simple-table v-bind="props.tableAttrs">
        <caption v-if="props.caption">{{props.caption}}</caption>
        <vt-table-head/>
        <vnodes :vnodes="props.slots.beforeBody"/>
        <vt-table-body ref="vt_table_body"/>
        <vnodes :vnodes="props.slots.afterBody"/>
    </v-simple-table>

</template>

<script>
    import VtTableHead from 'vue-tables-2/compiled/components/VtTableHead'
    import VtTableBody from 'vue-tables-2/compiled/components/VtTableBody'

    export default {
        name: "VtTable",
        props: ['props'],
        components: {
            VtTableHead,
            VtTableBody,
            vnodes: {
                functional: true,
                render: (h, ctx) => ctx.props.vnodes
            }
        }
    }
</script>
```

VtPagination:

```markup
<template>
        <v-pagination
            v-if="props.totalPages>1"
                :length="props.totalPages"
                v-model="props.page"
                total-visible="10"
                color="primary"
                @input="page => props.setPage(page)"/>
</template>

<script>
    export default {
        name: "VtPagination",
        props: ['props']
    }
</script>
```

VtPerPageSelector:

```markup
<template>
        <div class="VueTables__limit-field">
            <v-select :id="props.selectAttrs.id"
                      :value="props.selectAttrs.value"
                      label="Per Page"
                      @change="props.selectEvents.change"
                      :items="props.perPageValues"
            />
        </div>
</template>

<script>
    export default {
        name: "VtPerPageSelector",
        props: ['props']
    }
</script>
```

VtGenericFilter (remove label):

```markup
<template>
    <div class="VueTables__search-field">
        <input class="VueTables__search__input"
               type="text"
               placeholder="Search..."
               @keyup="e=>props.search(props.debounce)(e)"
               :id="`VueTables__search_${props.id}`"
               autocomplete="off"
        />
    </div>
</template>

<script>
    export default {
        name: "VtGenericFilter",
        props: ['props']
    }
</script>
```

VtSortControl:

```markup
<template>
    <span class="VueTables__sort">
     <v-icon v-if="props.sortable">
         {{icon}}
     </v-icon>
    </span>
</template>

<script>
    export default {
        name: "VtSortControl",
        props: ['props'],
        computed:{
            icon() {
                 // if not sorted return base icon
                if (!this.props.sortStatus.sorted) return 'mdi-sort';

                // return sort direction icon
                return this.props.sortStatus.asc ? 'mdi-sort-ascending' : 'mdi-sort-descending';
            }
        }
    }
</script>
```

Register the components:

```javascript
Vue.use(ClientTable, {}, false, 'bootstrap4', {
        table: VtTable,
        genericFilter: VtGenericFilter,
        dataTable: VtDataTable,
        sortControl: VtSortControl,
        pagination: VtPagination,
        perPageSelector: VtPerPageSelector
    })
```

### Filter on button click

By default, the generic filter is triggered on key up. Here is how to trigger it on click instead:

VtGenericFilter.vue:

```markup
<template>
    <div class="VueTables__search-field">
        <label :for="`VueTables__search_${props.id}`" :class="props.theme.label">
            {{props.display("filter")}}
        </label>

        <input :class="`VueTables__search__input ${props.theme.input} ${props.theme.small}`"
               type="text"
               v-model="query"
               :placeholder="props.display('filterPlaceholder')"
               :id="`VueTables__search_${props.id}`"
               autocomplete="off"
        />
        <button class="btn btn-primary" @click="e=>props.search(0)({
        target:{
            value:this.query
        }
        })">Search
        </button>
    </div>
</template>

<script>
    export default {
        name: "VtGenericFilter",
        props: ['props'],
        data() {
            return {
                query: ''
            }
        }
    }
</script>
```

Register the component:

```javascript
Vue.use(ClientTable, {}, false, 'bootstrap4', {
    genericFilter: VtGenericFilter
})
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://matanya.gitbook.io/vue-tables-2/custom-template.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
