0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Vue.js 2.3以降】親子コンポーネントのプロパティ受け渡しは.syncでやると楽になる。【Vuetify】date-picker実装例

Last updated at Posted at 2021-08-13

公式ページおよび結論

2.3.0から新規追加

“双方向バインディング”がプロパティに対して必要な場合もあります。残念ながら、本当の双方向バインディングはメンテナンスの問題を引き起こす可能性があります。子コンポーネントは親でも子でもその変更元が明らかでなくても親を変更させることができるからです。

このため代わりに update:myPropName というパターンでイベントを発火させる事をお薦めします。例えば title というプロパティを持つ仮のコンポーネントがあった場合、意図的に新しい値を割り当てる事ができます

実装例

Vuetify(1.5、ドキュメント比較するとおそらくVuetify2.x以降でも動くと思う)のdate-pickerを用いて妙に長く書かないといけないdate-pickerをコンポーネント化する。

※以下実装例はNuxt.jsの書き方で記述する。他Vue.js関連フレームワークであっても、プロパティなどの考え方は一緒だと考える。

親側

<template>
  <!-- 子であるdatepickerを設定する。このとき.syncを付けておくのが鍵。 -->
  <datepicker :date.sync="date"></datepicker>
  <!-- 他、自作コンポーネントdatepickerの配置および呼び出しなど色々あるけど中略 -->
</template>

<script>
export default {
  data() {
    return {
      // 親側のdate。こちらに反映される
      date:''
    }
  }
}

</script>

子側

<!-- 殆どはvuetify公式ページにある
  「テキスト欄クリックでカレンダーがポップアップし、
   日付押下すると日付がテキスト欄に適用される部品」 -->
<template>
  <v-menu
    ref="datetime"
    v-model="datetime"
    :close-on-content-click="false"
    transition="scale-transition"
    offset-y
    min-width="auto"
  >
    <template v-slot:activator="{ on }">
      <!-- ここでのv-modelには親からのプロパティは直接指定しない。 -->
      <v-text-field
        autocomplete="off"
        v-model="_date"
        prepend-icon="event"
        v-on="on"
        readonly
        clearable
      ></v-text-field>
    </template>
    <v-date-picker
      v-model="_date"
      locale="ja-jp"
      :day-format="nowdate => new Date(nowdate).getDate()"
      no-title
      scrollable
      @input="datetime = false"
    >
    </v-date-picker>
  </v-menu>
</template>

<style scoped></style>

<script>
// emit時のupdate呼び出し文字列
const emitUpdate = 'update:'
// 親からのプロパティ名集
const propName= {
  date:'date'
}

export default {
  // 親からもらうプロパティ名を指定
  props: [propName.date],
  data() {
    return {
      datetime: false
    }
  },
  computed: {
    // 子コンポーネント側の算出プロパティをget,setで制御
    _date: {
      // 親からプロパティを貰ったら反映
      get: function() {
        return this.date
      },
      // 子で値が変更されたなら、その代わりにemit発火によって親へとupdate
      // update対象をプロパティごとに指定できるため、複数プロパティにも対応可能
      set: function(value) {
        this.$emit(emitUpdate + propName.date, value)
      }
    }
  }
}
</script>

子側で色々書いておけば、親側で使う時はコンポーネント呼び出しと引数設定をしておけば子側で処理した値を引っ張ってこれる。まさに理想。

結論にいたるまでの$emitの基本と面倒なところ

vue.jsにおいて、本当の双方向バインディングはメンテナンスの問題を引き起こす可能性があるため推奨されておらず、上手く動かないことが多い。

自身の認識では、emitは子コンポーネントから親コンポーネントへと「〇〇イベントを実行してください」といった発火イベントを起こすことができると考えている。
よっては基本的には親側のメソッドでイベント内容を記述する必要がある。

1:親が子へとプロパティを渡す(そのプロパティ自体は親コンポーネントのプロパティであるため、他コンポーネントが変更を行ってしまっては、何処で変更されたかといった情報が読み取れなくなってしまう)
2:子が処理を行った後、emitによって親へと「〇〇というイベントが親にはありますよね、実行してください」と発火させる
3:親が自身のイベントを実行し、親プロパティへの反映や親側の処理を起動させる

ただ、親子間での単純なプロパティ変更である場合、上記のような処理記述は面倒でしかないし、再利用時のバグになりかねない。
.syncの機能はそこらへんを簡略化してくれていると考えている。

0
2
0

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
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?