Help us understand the problem. What is going on with this article?

Vue.jsでグリッド(table)をクリックするとモーダルで編集させるtips

More than 1 year has passed since last update.

まえがき

まだ完全版には遠いですが、役に立ちそうなのでメモ。

・・・作っては見たものの、結局モーダルの中に更にテーブルが必要になって、面倒になり、その画面は後に発見したjson-editorに変更しました。

まだまだVue.jsを完全に理解できた段階まではいかないため、おかしなことをしてる可能性があります。つまり、いっこーにっこー参考程度にしておいてくださいな。

前提

jQuery, Bootstrap3, Vue が読み込まれている。
もちろんほとんどの部分はBootstrapナシでもできますが、モーダルのコードはVue公式リファレンスのモーダルを元に、クラスはBootstrap3仕様に変更してあります。

新規行を作成する部分は未実装です。消すか作っちゃってください。

コンポーネント定義

使いたいページから見えるところにコードを入れてください。

まずはHTML内にテンプレート

template
<script type="text/x-template" id="modal-template">
    <transition name="modal">
        <div class="modal modal-mask modal-lg" style="display: block">
            <div class="modal-dialog">
                <div class="modal-content">

                    <div class="modal-header">
                        <slot name="header" class="modal-title">
                        </slot>
                    </div>

                    <div class="modal-body">
                        <slot name="body">
                        </slot>
                    </div>

                    <div class="modal-footer">
                        <slot name="footer">
                            <button type="button" class="btn btn-primary modal-default-button" @click="$emit('close')">
                                Close
                            </button>
                        </slot>
                    </div>
                </div>
            </div>
        </div>
    </transition>
</script>

<script type="text/x-template" id="grid-template">
    <div>
        <table class="editable">
            <thead>
                <tr>
                    <th v-for="key in columns"
                        @click="sortBy(key)"
                        :class="{ active: sortKey == key }">
                        {{ columnNames[key] }}
                        <span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
                        </span>
                    </th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="entry in filteredData"
                    @click="showModal(entry)">
                    <td v-for="key in columns">
                        {{entry[key]}}
                    </td>
                </tr>
                <tr>
                    <td v-bind:colspan="columns.length" @click="newRow" class="new-row">+ 行を追加</td>
                </tr>
            </tbody>
        </table>
        <modal v-if="showEditModal" @close="showEditModal = false">
            <h3 slot="header">{{modalTitle}}</h3>
            <div slot="body">
                <div v-for="key in editItems" class="form-group">
                    <label class="col-xs-2">{{columnNames[key]}}</label><input class="form-control col-xs-10" type="text" v-model="modalItem[key]" />
                </div>
            </div>
        </modal>
    </div>
</script>

コンポーネント定義のjavascript (もちろんvue を new する前に)

vue-grid-modal-editor.js
        Vue.component('modal', {
            template: '#modal-template'
        })

        Vue.component('edit-grid', {
            template: '#grid-template',
            props: {
                data: Array,
                columnNames: Object,
                columns: Array,
                editItems: Array,
                filterKey: String,
                modalTitle: String
            },
            data: function () {
                var sortOrders = {}
                this.columns.forEach(function (key) {
                    sortOrders[key] = 1
                })
                return {
                    sortKey: '',
                    sortOrders: sortOrders,
                    showEditModal: false,
                    modalItem: {},
                }
            },
            computed: {
                filteredData: function () {
                    var sortKey = this.sortKey
                    var filterKey = this.filterKey && this.filterKey.toLowerCase()
                    var order = this.sortOrders[sortKey] || 1
                    var data = this.data
                    if (filterKey) {
                        data = data.filter(function (row) {
                            return Object.keys(row).some(function (key) {
                                return String(row[key]).toLowerCase().indexOf(filterKey) > -1
                            })
                        })
                    }
                    if (sortKey) {
                        data = data.slice().sort(function (a, b) {
                            a = a[sortKey]
                            b = b[sortKey]
                            return (a === b ? 0 : a > b ? 1 : -1) * order
                        })
                    }
                    return data
                }
            },
            filters: {
            },
            methods: {
                sortBy: function (key) {
                    this.sortKey = key
                    this.sortOrders[key] = this.sortOrders[key] * -1
                },
                showModal: function (entry) {
                    this.showEditModal = true
                    this.modalItem = entry
                },
                newRow: function () {
                    alert("Create New Row!") // 新規行は未実装
                }
            }
        })
vue-grid-modal-editor.css
table.editable {
    border: 2px solid #42b983;
    border-radius: 3px;
    background-color: #fff;
}


    table.editable th, td {
        min-width: 120px;
        padding: 10px 20px;
        border: 1px solid #fff;
    }

    table.editable th {
        background-color: #42b983;
        color: rgba(255,255,255,0.66);
        cursor: pointer;
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
    }

    table.editable tr {
        background-color: #f9f9f9;
    }

    table.editable tbody tr:hover {
        cursor: pointer;
        text-decoration: underline;
        color: black;
        background-color: rgba(255,255,255,0.66);
    }


    table.editable th.active {
        color: #fff;
    }

        table.editable th.active .arrow {
            opacity: 1;
        }

    table.editable .arrow {
        display: inline-block;
        vertical-align: middle;
        width: 0;
        height: 0;
        margin-left: 5px;
        opacity: 0.66;
    }

        table.editable .arrow.asc {
            border-left: 4px solid transparent;
            border-right: 4px solid transparent;
            border-bottom: 4px solid #fff;
        }

        table.editable .arrow.dsc {
            border-left: 4px solid transparent;
            border-right: 4px solid transparent;
            border-top: 4px solid #fff;
        }


.modal-mask {
    position: fixed;
    z-index: 9998;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, .5);
    display: table;
    transition: opacity .3s ease;
}

使用方法と動作サンプル

デモ(JSFiddle)を作りました

blue-ossan
嫁さん一番、仕事は二番、三児のオヤジ。 徳島のとある里山に移住。古民家DIY改修中。 プログラミング16年、PHP+Laravelは相棒。javascriptは愛人。 良いプログラミングをするには良い食事で頭作りから!
https://pochaneco.space
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away