Vue.jsのコンポーネントの作り方（たぶん初級編）

##はじめに
　前回の記事「[jQueryからVue.jsへ乗り換えてみた](https://qiita.com/mishikawa-Horornis/items/73f1eb4fe6ff4b68ea28)」の続きです。
　わかりやすいソースを目指しますが、わかりにくかったら言ってください。あと、レンタルサーバーなどで使うことも考慮しております。実は自分がレンタルサーバー使ってるので…。あとwebｐackはまだ私も手を出していないです…。

## とりあえずベタ書きしてみる

　前回の数字の入力が、フォームのテキストなので見栄えが微妙でしたので`Vueコンポーネント`を作ります。とりあえず`0〜10までの値`をボタンでを加減できるようにしてみましょう。
　

```javascript
    Vue.component('toggle-button', {
        data() {
            return;
        },
        template: // コンポーネントの中身をHTML形式で書く
            '<div id="toggle-button">' +
            '   <button @click="set_value(-1)" v-bind:disabled="sub_disabled">ー</button>' +
            '   <span> {{ locale_string }} </input>' +
            '   <button @click="set_value(1)" v-bind:disabled="add_disabled">＋</button>' +
            '</div>',
        props: {
            'value': {
                type: Number,
                required: true,
                default: 0
            },
            'locale_string': {
                type: String,
                required: true
            },
            'add_disabled': {
                type: Boolean,
                default: true
            },
            'sub_disabled': {
                type: Boolean,
                default: false
            }
        },
        computed: { // 自動計算
            locale_string: function () {
                // 表示は半角スペースをいれて2桁にしている。
                return `${`\u00A0${Number(this.value).toString()}`.slice(-2)}`; 
            },
            add_disabled: function () {
                // valueが10以上なら＋をクリック無効にする
                return this.value >= 10;
            },
            sub_disabled: function () {
                // valueが10以上なら＋をクリック無効にする
                return this.value <= 0;
            }
        },
        methods: { //メソッド（イベントなどを渡す関数）
            addValue: function (add) {
                this.value += add;
                // valueが変わったら呼び出し元に反映させるおまじない。
                this.$emit('input', this.value);
            }
        }
    })
```
　これで`<toggle-button>`タグがVueコンポーネントとして登録されます。
　実際に表示させるとこんな感じになります。
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359920/38d51ac4-7a5e-225a-79d3-cbedcf483ea3.png)

　そして、これを前回のソースに反映させると…。

```html
<!DOCTYPE html>
<html lang="jp">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<script>
    Vue.component('toggle-button', {
        data() {
            return;
        },
        template: // コンポーネントの中身をHTML形式で書く
            '<div id="toggle-button">' +
            '   <button @click="set_value(-1)" v-bind:disabled="sub_disabled">ー</button>' +
            '   <span> {{ locale_string }} </input>' +
            '   <button @click="set_value(1)" v-bind:disabled="add_disabled">＋</button>' +
            '</div>',
        props: {
            'value': {
                type: Number,
                required: true,
                default: 0
            },
            'locale_string': {
                type: String,
                required: true
            },
            'add_disabled': {
                type: Boolean,
                default: true
            },
            'sub_disabled': {
                type: Boolean,
                default: false
            }
        },
        computed: { // 自動計算
            locale_string: function () {
                // 表示は半角スペースをいれて2桁にしている。
                return `${`\u00A0${Number(this.value).toString()}`.slice(-2)}`;
            },
            add_disabled: function () {
                // valueが10以上なら＋をボタンをクリック無効にする
                return this.value >= 10;
            },
            sub_disabled: function () {
                // valueが10以上なら−ボタンをクリック無効にする
                return this.value <= 0;
            }
        },
        methods: { //メソッド（イベントなどを渡す関数）
            addValue: function (add) {
                this.value += add;
                // value値が変わったら呼び出し元に反映させるおまじない。
                this.$emit('input', this.value);
            }
        }
    })
</script>
<div id="app">
    <p v-for="(item, index) in items">
        <span>{{ item.callName }}：</span><toggle-button type="number" v-model.number="item.counts" style="display: inline;"></toggle-button><span class="subtotal"> {{ subtotalMoney[index] }} 円</span>
    </p>
    <p>
        <span>合計： {{ totalMoney }} </span>
    </p>
</div>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            items: [
                {callName: '1円', unit: 1, counts: 0},
                {callName: '5円', unit: 5, counts: 0},
                {callName: '10円', unit: 10, counts: 0},
                {callName: '50円', unit: 50, counts: 0},
                {callName: '100円', unit: 100, counts: 0},
                {callName: '500円', unit: 500, counts: 0,},
                {callName: '千円', unit: 1000, counts: 0},
                {callName: '弐千円', unit: 2000, counts: 0},
                {callName: '五千円', unit: 5000, counts: 0},
                {callName: '壱万円', unit: 10000, counts: 0}
            ]
        },
        computed: {
            totalMoney: function () {
                let total = 0;
                for (let i = 0; i < this.items.length; i++) {
                    total += this.items[i].unit * this.items[i].counts;
                }
                return Number(total).toLocaleString();
            },
            subtotalMoney: function () {
                let subtotals = [];
                for (let i = 0; i < this.items.length; i++) {
                    subtotals.push(Number(this.items[i].unit * this.items[i].counts).toLocaleString());
                }
                return subtotals;
            }
        }
    });
</script>
</body>
</html>
```
　お世辞にも綺麗とはいいがたいですが、こんな感じで部品が作れます。本当は.vueファイルにまとめた方が良いのですが、レンタルサーバだとうまくいかないことがあるのでそのままscriptタグでくくって書いています。

## あとは想像力
　コンポーネント作りにハマるとあれもこれも部品化したくなるのですが、見た目を良くするならBootstrap とか使うといろいろ捗ると思います。

## 参考
　Vue.js コンポーネントの基本： https://jp.vuejs.org/v2/guide/components.html