LoginSignup
17

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-02-21

まえがき

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

・・・作っては見たものの、結局モーダルの中に更にテーブルが必要になって、面倒になり、その画面は後に発見した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)を作りました

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17