PR

WordPressの投稿にソートやページネーションの機能を持ったテーブルを実装する

Vue3
スポンサーリンク
スポンサーリンク

 

 

 

環境

このページで使用しているフレームワークやライブラリのバージョンは、以下のとおりです。

vue.js3.2.31
vue3-sfc-loader0.8.4
vue-good-table-next0.1.0

vue-good-table-nextとは?

vue-good-table-next」は、ソート、ページネーション、フィルターなどの機能をもったテーブルを実装できるVueライブラリです。
「vue-good-table-next」は、「Vue2.x」で動作する「vue-good-table」を「Vue3.x」に対応させたバージョンです。
GitHub(2022年8月14日時点)の「vue-good-table-next」バージョンは「0.1.0」で、"README"にも以下の注意書きがあります。

This project is a port of Vue-good-table for Vue 3.x. The current version is stable enough to start developing new projects but work is still in progress and changes could be made in the short-term. Thus the project is not yet intended to be used in a production environment.

ただ、基本的な動作は安定しているようです。

テーブル用ライブラリの比較

多機能なテーブルを実装するためのライブラリやプラグインは、以下のようにいろいろ提供されています。

この記事では「vue-good-table-next」を紹介しますが、各種のライブラリの特徴を比較表にまとめました。(2022年8月14日時点)
なお、この比較表は、「vue-good-table-next」をインポートしたSFCで表示しています。

カスタマイズの自由度では「vue-tables2(v-tables3)」が優れているのですが、Vue3の対応が不完全なので、総合的に「vue-good-table-next」を選びました。

WordPressの投稿でvue-good-table-nextを使う

上記の各種ライブラリの比較表の部分には、WordPressのブロックエディタで以下の「カスタム HTML」ブロックを配置しています。

<script src="https://cdn.jsdelivr.net/npm/vue@3.2.31/dist/vue.global.js"></script>

<div id="app"></div>

<script type="module">
    ////////////////////////////////////////////////////////////////////////////////
    // vue3-sfc-loader モジュール
    ////////////////////////////////////////////////////////////////////////////////
    import { loadModule } from "https://cdn.jsdelivr.net/npm/vue3-sfc-loader@0.8.4/dist/vue3-sfc-loader.esm.js";

    ////////////////////////////////////////////////////////////////////////////////
    // vue3-sfc-loader オプション
    // SFCファイルから外部のモジュールをimportできるオプション
    // 参考:https://github.com/FranckFreiburger/vue3-sfc-loader/issues/14#issuecomment-908849863
    ////////////////////////////////////////////////////////////////////////////////
    const vue3_sfc_loader_options = {
        moduleCache: { vue: Vue },
        getFile(url) {
            url = /.*?\.js|.mjs|.css|.less|.vue$/.test(url)
                ? url
                : `${url}.vue`;
            const type = /.*?\.js|.mjs$/.test(url)
                ? ".mjs"
                : /.*?\.vue$/.test(url)
                ? ".vue"
                : /.*?\.css$/.test(url)
                ? ".css"
                : ".vue";
            const getContentData = (asBinary) =>
                fetch(url).then((res) =>
                    !res.ok
                        ? Promise.reject(url)
                        : asBinary
                        ? res.arrayBuffer()
                        : res.text()
                );
            return { getContentData: getContentData, type: type };
        },
        addStyle(textContent) {
            let styleElement = document.createElement("style");
            document.head.insertBefore(
                Object.assign(styleElement, { textContent }),
                document.head.getElementsByTagName("style")[0] || null
            );
        },
        handleModule(type, getContentData, path, options) {
            switch (type) {
                case ".css":
                    return options.addStyle(getContentData(false));
                case ".less":
                    console.error(".......");
            }
        },
        log(type, ...args) {
            console.log(type, ...args);
        },
    };

    ////////////////////////////////////////////////////////////////////////////////
    // Vue.js アプリケーションインスタンス
    ////////////////////////////////////////////////////////////////////////////////
    const app = Vue.createApp({
        components: {
            "table-library-comparison-table": Vue.defineAsyncComponent(() =>
                loadModule(
                    "../wp-content/themes/cocoon-child-master/vue-components/table_library_comparison_table.vue",
                    vue3_sfc_loader_options
                )
            ),
        },
        template: `<table-library-comparison-table />`,
    });
    app.mount("#app");
</script>

いつものように、「vue3-sfc-loader」で"table_library_comparison_table.vue"を読み込んでいます。

“table_library_comparison_table.vue”の中身

"table_library_comparison_table.vue"の全体です。

<script setup>
////////////////////////////////////////////////////////////////////////////////
// vue-good-table-next モジュール
////////////////////////////////////////////////////////////////////////////////
import { VueGoodTable } from "https://cdn.jsdelivr.net/npm/vue-good-table-next@0.1.0/dist/vue-good-table.cjs.min.js";

////////////////////////////////////////////////////////////////////////////////
// data
////////////////////////////////////////////////////////////////////////////////
const table_columns = [
    {
        label: "比較項目",
        field: "comparison_items",
        thClass: "vgt-center-align vgt-comparison-items",
        tdClass: "vgt-center-align vgt-comparison-items",
        sortable: true,
        html: true,
    },
    {
        label: "vue-good-table-next",
        field: "vue_good_table_next",
        thClass: "vgt-center-align vgt-vue-good-table-next",
        tdClass: "vgt-center-align vgt-vue-good-table-next",
        html: true,
    },
    {
        label: "vue-tables2",
        field: "vue_tables2",
        thClass: "vgt-center-align vgt-vue-tables2",
        tdClass: "vgt-center-align vgt-vue-tables2",
        html: true,
    },
    {
        label: "Vuetable-2",
        field: "vuetable_2",
        thClass: "vgt-center-align vgt-vuetable-2",
        tdClass: "vgt-center-align vgt-vuetable-2",
        html: true,
    },
    {
        label: "Grid.js",
        field: "grid_js",
        thClass: "vgt-center-align vgt-grid-js",
        tdClass: "vgt-center-align vgt-grid-js",
        html: true,
    },
];
const table_rows = Vue.ref([]);
const search_options = {
    enabled: true,
    trigger: "enter",
    placeholder: "テーブルを検索します",
};
const sort_options = {
    enabled: false,
};
const pagination_options = {
    enabled: true,
    mode: "pages",
    perPage: -1, // -1のときは全項目
    position: "top",
    perPageDropdown: [3, 5],
    dropdownAllowAll: true,
    setCurrentPage: 1,
    nextLabel: "次へ",
    prevLabel: "前へ",
    rowsPerPageLabel: "1ページに表示する比較項目数",
    ofLabel: " / ",
    pageLabel: "ページ", // for 'pages' mode
    allLabel: "全項目",
};

////////////////////////////////////////////////////////////////////////////////
// onMountedライフサイクルフック
////////////////////////////////////////////////////////////////////////////////
Vue.onMounted(() => {
    table_rows.value = [
        {
            comparison_items: "Vue3対応",
            vue_good_table_next: "〇<br>基本的な動作は安定",
            vue_tables2: "<span style='font-size: 1.2em;'>△</span><br>v-tables3で対応しているが不完全",
            vuetable_2: "<span style='font-size: 1.8em;'>×</span>",
            grid_js: "〇<br>オリジナルがJavaScriptライブラリなので、Vue3のコンポーネントでラップ可能"
        },
        {
            comparison_items: "ソート",
            vue_good_table_next: "〇",
            vue_tables2: "〇",
            vuetable_2: "〇",
            grid_js: "〇"
        },
        {
            comparison_items: "ページネーション",
            vue_good_table_next: "〇",
            vue_tables2: "〇",
            vuetable_2: "〇",
            grid_js: "〇"
        },
        {
            comparison_items: "グループ表示",
            vue_good_table_next: "〇",
            vue_tables2: "<span style='font-size: 1.2em;'>◎</span><br>指定の要素で自動的にグループ化",
            vuetable_2: "?",
            grid_js: "<span style='font-size: 1.8em;'>×</span>"
        },
        {
            comparison_items: "ヘッダー/セルの<br>HTML対応",
            vue_good_table_next: "〇",
            vue_tables2: "〇",
            vuetable_2: "〇",
            grid_js: "〇"
        },
        {
            comparison_items: "テーブル構成の<br>カスタマイズ",
            vue_good_table_next: "〇<br>ある程度可能",
            vue_tables2: "<span style='font-size: 1.2em;'>◎</span><br>カスタムテンプレートで自由度大",
            vuetable_2: "?",
            grid_js: "<span style='font-size: 1.8em;'>×</span>"
        },
    ];
});
</script>

<template>
    <VueGoodTable v-bind:columns="table_columns" v-bind:rows="table_rows" v-bind:search-options="search_options"
        v-bind:sort-options="sort_options" v-bind:pagination-options="pagination_options">
        <template #table-column="props">
            <span v-if="props.column.label == 'vue-tables2'">
                {{ props.column.label }}<br>(v-tables3)
            </span>
            <span v-else>
                {{ props.column.label }}
            </span>
        </template>
    </VueGoodTable>
</template>

<style>
@import "https://cdn.jsdelivr.net/npm/vue-good-table-next@0.1.0/dist/vue-good-table-next.css";

table.vgt-table th,
table.vgt-table td {
    vertical-align: middle !important;
}

th.vgt-comparison-items {
    min-width: 10em !important;
}

th.vgt-vue-good-table-next,
th.vgt-vue-tables2,
th.vgt-vuetable-2,
th.vgt-grid-js {
    min-width: 15em !important;
}

td.vgt-vue-good-table-next,
td.vgt-vue-tables2,
td.vgt-vuetable-2,
td.vgt-grid-js {
    font-size: 0.8em;
}
</style>
////////////////////////////////////////////////////////////////////////////////
// vue-good-table-next モジュール
////////////////////////////////////////////////////////////////////////////////
import { VueGoodTable } from "https://cdn.jsdelivr.net/npm/vue-good-table-next@0.1.0/dist/vue-good-table.cjs.min.js";

"vue-good-table-next"モジュールをCDNサイト「https://cdn.jsdelivr.net」からインポートします。

<template>
    <VueGoodTable v-bind:columns="table_columns" v-bind:rows="table_rows" v-bind:search-options="search_options"
        v-bind:sort-options="sort_options" v-bind:pagination-options="pagination_options">
        <template #table-column="props">
            <span v-if="props.column.label == 'vue-tables2'">
                {{ props.column.label }}<br>(v-tables3)
            </span>
            <span v-else>
                {{ props.column.label }}
            </span>
        </template>
    </VueGoodTable>
</template>

インポートした"VueGoodTable"を<template>タグで配置します。

基本は、作成したSFCのローカル変数を"VueGoodTable"のプロパティーにバインドしているだけですが、

        <template #table-column="props">
            …
        </template>

の部分については、「vue-good-table-next」の公式ドキュメントの「Custom column headers」を参照してください。

また、テーブルのコンテンツ("table_rows")は、あえて"Vue.onMounted();"の中で設定しました。
実際の用途では、"table_rows"を動的に変更すると、テーブルのコンテンツがリアクティブに再表示されます。

まとめ

「vue-good-table-next」を使うと、WordPressの投稿に多機能なテーブルを手軽に実装できます。
しかも、Vueを使っているので、テーブルのコンテンツも簡単に更新/再表示ができます。

コメント