jQueryと比較するため、Vue.jsで本ショッピングカートの編集機能を作成してみた

![カート.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313622/a0b5d606-73fe-b388-89b9-4dac00675883.png)

上図のように下記の機能を実現します
・カートにいれている本の情報一覧を表示する
・合計金額を表示する
・本ごとの数量を画面で編集できる
・[-]ボタン、[数量]、 [+]ボタンをコンポーネントで実現する
・[-]ボタンをクリックすると数量を1減らす
・[+]ボタンをクリックすると数量を1増やす
・数量1以下のとき、[-]ボタンは無効とする
・数量変更と同時に合計金額も更新する
・[削除]ボタンをクリックすると該当本を一覧から削除し、合計金額を更新する
・すべての本の情報が一覧から削除されたら、画面に「データなし」のみ表示する

```html:vue01.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>カート</title>
    <style type="text/css">
        table {
            border: 2px solid #999999;
            border-collapse: collapse;
            margin: 0 auto;
        }

        th, td {
            padding: 8px 16px;
            border: 1px solid #999999;
        }

        th {
            background-color: #CCCCCC;
        }

        [v-cloak] {
            display: none;
        }
    </style>
</head>
<body>

<div id="app" v-cloak> <!v-cloak: 処理をすべて終えてから画面に表示させる-->
    <br>
    <div v-if="books.length >0">
        <table border="1">
            <thead>
            <tr>
                <th>コード</th>
                <th>名称</th>
                <th>出版社</th>
                <th>定価</th>
                <th>数量</th>
                <th>操作</th>
            </tr>
            </thead>
            <tbody>
            <tr v-for="(book,index) in books">
                <td>{{book.code}}</td>
                <td>{{book.name}}</td>
                <td>{{book.publisher}}</td>
                <td>{{book.price | dspPrice}}</td>
                <td>
                    <cpn_count :count="book.count" :index="index" :key="book.code"
                               @count_changed="count_changed"></cpn_count>
                </td>
                <td>
                    <button @click="delBook(index)">削除</button>
                </td>
            </tr>
            </tbody>
        </table>
        <div style="text-align:center;"><h3>合計金額：{{totalPrice | dspPrice}}</h3></div>

    </div>
    <div v-else　style="text-align:center;"><h3>データなし</h3></div>

</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    // 数量編集コンポーネント（子コンポーネント）
    const cpn_count = Vue.extend({
        // `` で囲むことで複数行（ES6）
        template: `
            <div>
                 <button @click="decrease()" :disabled="d_count <=1">-</button>
                    {{ d_count }}
                 <button @click="increase()">+</button>
            </div>`,
        props: {
            count: {
                type: Number,
                default: 0
            },
            index: {
                type: Number,
                default: 0
            }
        },
        data() {
            return {
                d_count: this.count
            }
        },
        methods: {
            increase() {
                this.d_count++;
                this.emit_count_changed()
            },
            decrease() {
                this.d_count--
                this.emit_count_changed()
            },
            // イベントを発生させる
            emit_count_changed() {
                let info = {
                    index: this.index,
                    count: this.d_count
                }
                this.$emit('count_changed', info)
            }
        },
     })

    // テスト用データを作成
    let books = [
        {
            code: '00009',
            name: 'はじめてのC',
            publisher: '技術評論社',
            price: 2178,
            count: 1
        },
        {
            code: '01002',
            name: 'やさしいC＋＋',
            publisher: 'SOFTBANK',
            price: 2500,
            count: 1
        },
        {
            code: '20006',
            name: '実践SQL',
            publisher: 'SOFTBANK',
            price: 2800,
            count: 1
        }
    ]
    // ルートコンポーネントのインスタンスを生成
    const app = new Vue({
        // Django templateでVue.jsを利用する場合、デリミタを変更する必要がある
        // delimiters: ['[[', ']]'],
        // {{book.code}} -> [[book.code]] など
        el: '#app',
        data: {
            books: books
        },
        methods: {
            count_changed(info) {
                this.books[info.index].count = info.count
            },
            delBook(index) {
                this.books.splice(index, 1)
            }
        },
        filters: {
            // 金額を表示形式に変更
            dspPrice(price) {
                return "￥" + price.toLocaleString();
            },
        },
        computed: {
            // 金額合計計算
            totalPrice() {
                let totalValue = this.books.reduce(function (preval, book) {
                    return preval + book.price * book.count
                }, 0)
                return totalValue
            }
        },
        components: {
            // 数量編集コンポーネントをローカル登録
            'cpn_count': cpn_count
        }
    })
</script>

</body>
</html>

```

感想：
　　・コンポーネント化できるので、重複利用する際には便利です　（Vb.net,C#のユーザーコントロールを作成すると同じ感覚）
　　・html側の値とJavaScriptの処理が連動できます　（「数量変更と同時に合計金額も更新する」などの処理はVueがやってくれます）
　　・ロジックだけの記述で、機能を実現できるので、可読性がいいです