Skip to content

Instantly share code, notes, and snippets.

@Bunix
Created September 11, 2020 14:37
Show Gist options
  • Save Bunix/04d9bc31cdc9b2c2bf25fd0110bbdaa3 to your computer and use it in GitHub Desktop.
Save Bunix/04d9bc31cdc9b2c2bf25fd0110bbdaa3 to your computer and use it in GitHub Desktop.

Revisions

  1. @plmrlnsnts plmrlnsnts revised this gist Sep 10, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion vue-datatable.md
    Original file line number Diff line number Diff line change
    @@ -29,7 +29,7 @@ Keep in mind that this implementation requires an api for fetching data, and ass

    ```vue
    <template>
    <data-table v-if="response" v-bind="{ columns, ...response } v-on:refetch="url = $event">
    <data-table v-if="response" v-bind="{ columns, ...response }" @refetch="url = $event">
    <!-- The value of a resource attribute will be displayed by default. -->
    <!-- A 'cell' slot is available if you need full control on how each cell should look like -->
    <template v-slot:cell="{ resource, column }">
  2. @plmrlnsnts plmrlnsnts revised this gist Sep 10, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion vue-datatable.md
    Original file line number Diff line number Diff line change
    @@ -32,7 +32,7 @@ Keep in mind that this implementation requires an api for fetching data, and ass
    <data-table v-if="response" v-bind="{ columns, ...response } v-on:refetch="url = $event">
    <!-- The value of a resource attribute will be displayed by default. -->
    <!-- A 'cell' slot is available if you need full control on how each cell should look like -->
    <template v-slot:cell="{ resource, column }>
    <template v-slot:cell="{ resource, column }">
    <div>Some fancy thing is happening here<div>
    </template>
    </data-table>
  3. @plmrlnsnts plmrlnsnts revised this gist Sep 10, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion vue-datatable.md
    Original file line number Diff line number Diff line change
    @@ -29,7 +29,7 @@ Keep in mind that this implementation requires an api for fetching data, and ass

    ```vue
    <template>
    <data-table v-if="response" v-bind="{ columns, ...response } v-on:refetch="url = $event">
    <data-table v-if="response" v-bind="{ columns, ...response } v-on:refetch="url = $event">
    <!-- The value of a resource attribute will be displayed by default. -->
    <!-- A 'cell' slot is available if you need full control on how each cell should look like -->
    <template v-slot:cell="{ resource, column }>
  4. @plmrlnsnts plmrlnsnts revised this gist Sep 10, 2020. 1 changed file with 10 additions and 5 deletions.
    15 changes: 10 additions & 5 deletions vue-datatable.md
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,9 @@ Keep in mind that this implementation requires an api for fetching data, and ass

    ```json
    {
    "perPageOptions": [15, 50, 100],
    "perPageOptions": [
    15, 50, 100
    ],
    "actions": [
    "Delete all", "Publish All", "Unpublish All"
    ],
    @@ -27,10 +29,13 @@ Keep in mind that this implementation requires an api for fetching data, and ass

    ```vue
    <template>
    <data-table
    v-if="response"
    v-bind="{ columns, ...response }
    @refetch="url = $event" />
    <data-table v-if="response" v-bind="{ columns, ...response } v-on:refetch="url = $event">
    <!-- The value of a resource attribute will be displayed by default. -->
    <!-- A 'cell' slot is available if you need full control on how each cell should look like -->
    <template v-slot:cell="{ resource, column }>
    <div>Some fancy thing is happening here<div>
    </template>
    </data-table>
    </template>
    <script>
  5. @plmrlnsnts plmrlnsnts revised this gist Sep 10, 2020. 1 changed file with 4 additions and 5 deletions.
    9 changes: 4 additions & 5 deletions vue-datatable.md
    Original file line number Diff line number Diff line change
    @@ -13,7 +13,7 @@ Keep in mind that this implementation requires an api for fetching data, and ass
    "sortDirection": "asc"
    },
    "resources": {
    "data": [...],
    "data": [],
    "from": 1,
    "to": 15,
    "total": 100,
    @@ -27,11 +27,10 @@ Keep in mind that this implementation requires an api for fetching data, and ass

    ```vue
    <template>
    <DataTable
    <data-table
    v-if="response"
    v-bind="{ columns, ...response }
    @refetch="url = $event"
    />
    v-bind="{ columns, ...response }
    @refetch="url = $event" />
    </template>
    <script>
  6. @plmrlnsnts plmrlnsnts revised this gist Sep 10, 2020. 1 changed file with 7 additions and 5 deletions.
    12 changes: 7 additions & 5 deletions vue-datatable.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    Keep in mind that this implementation requires an api for fetching data, and assumes the following response schema:

    ```
    ```json
    {
    "perPageOptions": [15, 50, 100],
    "actions": [
    @@ -27,7 +27,11 @@ Keep in mind that this implementation requires an api for fetching data, and ass

    ```vue
    <template>
    <DataTable v-if="response" v-bind="{ columns, ...response } />
    <DataTable
    v-if="response"
    v-bind="{ columns, ...response }
    @refetch="url = $event"
    />
    </template>
    <script>
    @@ -62,8 +66,6 @@ export default {
    ### Component

    ```vue
    // DataTable.vue
    <template>
    <div>
    <div>
    @@ -216,7 +218,7 @@ export default {
    if (! value) return
    if (window.confirm(`${value} selected items?`)) {
    this.$emit('action', value)
    this.$emit('click:action', value)
    }
    },
    }
  7. @plmrlnsnts plmrlnsnts created this gist Sep 10, 2020.
    225 changes: 225 additions & 0 deletions vue-datatable.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,225 @@
    Keep in mind that this implementation requires an api for fetching data, and assumes the following response schema:

    ```
    {
    "perPageOptions": [15, 50, 100],
    "actions": [
    "Delete all", "Publish All", "Unpublish All"
    ],
    "request": {
    "search": null,
    "perPage": 15,
    "sortAttribute": "title",
    "sortDirection": "asc"
    },
    "resources": {
    "data": [...],
    "from": 1,
    "to": 15,
    "total": 100,
    "next_page_url": "foo?page=2",
    "prev_page_url": null,
    }
    }
    ```

    ### Usage

    ```vue
    <template>
    <DataTable v-if="response" v-bind="{ columns, ...response } />
    </template>
    <script>
    import axios from 'axios'
    export default {
    data: vm => ({
    url: 'http:://link-to-your-api.json',
    response: null,
    columns: [
    { name: 'Title', attribute: 'title', sortable: 'title' },
    { name: 'Author', attribute: 'author', sortable: 'author' },
    { name: 'Price', attribute: 'price', sortable: 'price' },
    { name: 'Status', attribute: 'status' },
    ],
    }),
    watch: {
    url: {
    immediate: true,
    handler: function () {
    axios.get(this.url).then({ data } => {
    this.response = { ...data }
    })
    }
    }
    }
    }
    </script>
    ```

    ### Component

    ```vue
    // DataTable.vue
    <template>
    <div>
    <div>
    <!-- Header -->
    <div>
    <form @submit.prevent>
    <input type="text" :value="query.search" @input="query.search = $event.target.value" />
    </form>
    <form @submit.prevent v-if="selectedResources.length">
    <select v-model="selectedAction">
    <option :value="null">{{ selectedResources.length }} selected</option>
    <option v-for="action in actions" :key="`actions-${action}`">{{ action }}</option>
    </select>
    </form>
    </div>
    <!-- Table -->
    <table>
    <thead>
    <tr>
    <th width="1%">
    <input type="checkbox" :checked="isAllSelected" @click="isAllSelected ? deselectAll() : selectAll()"/>
    </th>
    <th v-for="column in columns" :key="`columns-${column.name}`">
    <a href="#" v-if="column.sortable" @click.prevent="sortBy(column.sortable)">
    <span>{{ column.name }}</span>
    <span v-if="query.sortAttribute === column.sortable">
    <svg width="16" height="16" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
    <path v-if="query.sortDirection === 'asc'" fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"></path>
    <path v-else fill-rule="evenodd" d="M14.707 12.707a1 1 0 01-1.414 0L10 9.414l-3.293 3.293a1 1 0 01-1.414-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 010 1.414z" clip-rule="evenodd"></path>
    </svg>
    </span>
    </a>
    <span v-else>
    {{ column.name }}
    </span>
    </th>
    </tr>
    </thead>
    <tbody>
    <tr v-for="resource in resources.data" :key="resource.id">
    <td>
    <input type="checkbox" :value="resource.id" v-model="selectedResources" />
    </td>
    <td v-for="column in columns" :key="`cell-${resource.id}-${column.name}`">
    <slot name="cell" v-bind="{ column, resource }">
    {{ resource[column.attribute] }}
    </slot>
    </td>
    </tr>
    </tbody>
    </table>
    <!-- Footer -->
    <div>
    <div>
    <span>Showing:</span>
    <form @submit.prevent>
    <select v-model="query.perPage">
    <option v-for="perPageOption in perPageOptions" :key="`per-page-options-${perPageOption}`" :value="perPageOption">
    {{ perPageOption }}
    </option>
    </select>
    </form>
    </div>
    <div>
    {{ `${resources.from}-${resources.to} of ${resources.total}` }}
    </div>
    <div>
    <button v-if="resources.prev_page_url" @click="getResources(resources.prev_page_url)">
    Previous
    </button>
    <span v-else>
    Previous
    </span>
    <button v-if="resources.next_page_url" @click="getResources(resources.next_page_url)">
    Next
    </button>
    <span v-else>
    Next
    </span>
    </div>
    </div>
    </div>
    </div>
    </template>
    <script>
    import { pickBy, debounce } from 'lodash'
    export default {
    props: {
    columns: Array,
    perPageOptions: Array,
    request: Object,
    resources: Object,
    },
    data: vm => ({
    query: vm.request,
    selectedAction: null,
    selectedResources: [],
    }),
    computed: {
    isAllSelected () {
    return this.resources.data.length === this.resources.data
    .filter(resource => this.isSelected(resource))
    .length
    },
    },
    methods: {
    isSelected (resource) {
    return this.selectedResources.includes(resource.id)
    },
    selectAll () {
    this.resources.data
    .filter(resource => ! this.isSelected(resource))
    .forEach(resource => this.selectedResources.push(resource.id))
    },
    deselectAll () {
    this.selectedResources.splice(0, this.selectedResources.length)
    },
    sortBy (sortAttribute) {
    if (this.query.sortAttribute !== sortAttribute) {
    this.query.sortAttribute = sortAttribute
    } else {
    this.query.sortDirection = this.query.sortDirection === 'asc' ? 'desc' : 'asc'
    }
    },
    getResources (url) {
    this.$emit('refetch', url)
    }
    },
    watch: {
    query: {
    deep: true,
    handler: debounce(function (value) {
    let url = window.location.href.split('?')[0]
    let params = new URLSearchParams(pickBy(value)).toString()
    this.getResources(`${url}?${params}`)
    }, 300)
    },
    selectedAction: function (value) {
    if (! value) return
    if (window.confirm(`${value} selected items?`)) {
    this.$emit('action', value)
    }
    },
    }
    }
    </script>
    ```