干し石榴長文用

長文以外はTumblrへ徐々に移します。

Vue.jsのカスタムコンポーネントでv-model

やりたいこと

要点

要点というか今になってようやくv-modelが「何をするシンタックスシュガーなのか」を理解しただけなのですが、カスタムコンポーネント側が以下の要件を満たせばOKみたいです。
カスタムコンポーネント側が$data.hogeで状態を持つと仮定して、

  • HTML標準のinput系要素と同じように振舞う
    • valueプロパティでhogeの初期値を受け取る
    • valueプロパティをwatchして都度hogeに通す
    • hogewatchしてinputイベントをemitする

サンプル

DatePickerMenu.vue ※Vuetify依存です

<template>
  <v-menu
    ref="menu"
    v-model="opened"
    :close-on-content-click="false"
    :nudge-right="40"
    :return-value.sync="date"
    lazy
    transition="scale-transition"
    offset-y
    full-width
    min-width="290px"
  >
    <template v-slot:activator="{ on }">
      <v-text-field
        v-model="date"
        :label="label"
        prepend-icon="event"
        readonly
        v-on="on"
      />
    </template>
    <v-date-picker v-model="date" no-title scrollable>
      <v-spacer />
      <v-btn flat @click="opened = false">
        キャンセル
      </v-btn>
      <v-btn flat color="primary" @click="$refs.menu.save(date)">
        OK
      </v-btn>
    </v-date-picker>
  </v-menu>
</template>

<script>
export default {
  name: 'DatePickerMenu',
  props: {
    label: {
      type: String,
      default: '日付'
    },
    value: {
      type: String,
      default: () => new Date().toISOString().substr(0, 10)
    }
  },
  data () {
    return {
      opened: false,
      date: this.value
    }
  },
  watch: {
    value (val) {
      this.date = val
    },
    date (val) {
      this.$emit('input', val)
    }
  }
}
</script>

Sample.vue

<template>
  <div>
    <h3>期間</h3>
    <date-picker-menu v-model="dateFrom" label="この日から" />
    <date-picker-menu v-model="dateTo" label="この日まで" />
  </div>
</template>

<script>
import DatePickerMenu from '@/components/DatePickerMenu'

export default {
  components: {
    DatePickerMenu
  },
  data () {
    // fromの初期値は1週間前
    let dateFrom = new Date()
    dateFrom.setDate(dateFrom.getDate() - 7)
    let dateTo = new Date()
    return {
      dateFrom: dateFrom.toISOString().substr(0, 10),
      dateTo: dateTo.toISOString().substr(0, 10)
    }
  },
  watch: {
    dateFrom () {
      console.log('dateFrom', this.dateFrom)
    },
    dateTo () {
      console.log('dateTo', this.dateTo)
    }
  }
}
</script>