Vue.js実際のところ(2)−コンポーネント間のデータのやりとり

今回は活動日付に着目して,データのコンポーネント間の値のやりとりを整理してみます.

前の投稿でコンポーネントの構成図を載せましたが,あの図でいう App.vue で activity という名称のdataを保持していて,そのデータを子コンポーネントに対してバインドしています. 日付のコンポーネントに注目すると下図の様な構成です.

1.の箇所は親側(App.vue)で次のように実装しています.

<datec @set-date="setDate" v-bind:date="activity.date"></datec>

v-bind:date=”activity.date”がバインド箇所で,子コンポーネントの dateプロパティに値をセットしています.

2.は当初子コンポーネント上で下記の様に書いてました.

props: {
  date: Date
},
data () {
  return {
    thedate: ''
  }
},
mounted () {
  this.thedate = this.date
}

プロパティとデータとそれぞれ設定して,コンポーネントがマウントされた時にプロパティの値(=親からセットされた値)をデータにセットしています.
(日付の初期値をどうセットすれば良いか判らなくて空文字列をセットしてますが,これnullが正解かと. 怪しいです.まだちゃんと調べてないです)

ただ,この方法だと既にコンポーネントがマウントされた状態で,親側App.vueのactivity.dateの値が何らかの理由で変更された時に子側の値が変わらないんですね. そこで当初は watch を使って次のように書いてました.

  watch: {
    date: function () {
      this.thedate = this.date
    }
  }

ところが,ふと次のように書いてみたらこれで問題無く動作している様なので,現状はこの実装になってます. mountedもwatchも使ってません.

props: {
  date: Date
},
data () {
  return {
    thedate: this.date
  }
}

ただ,アプリ全体の構成をこのタイミングで少し変更した(表示モードと入力モードを分けるようにして,右サイドバーで活動日をクリックされた時に,一度表示モードで見せて,「編集」ボタンをクリックされた時に入力モードになるようにした)ので,実装次第ではwatchが必要なケースがあると思います.

3.は次のようにv-model でバインドされています.

<template>
  <div>
    <h4 class="uk-heading-bullet">
      活動予定日
    </h4>
    <el-date-picker
    v-model="thedate"
    type="date"
    placeholder="活動予定日"
    @blur="doneEdit"
    @keyup.enter.native="doneEdit"
    >
    </el-date-picker>
  </div>
</template>

el-date-pickerはElementのタグで,入力系はほぼこの機能を利用しています. とてつもなく便利なので,オフィシャルサイトを是非チェックしてみてください.
このタグをさらっと書いておくだけで,下図の様なDatePickerが実装されます.

4.は結局親のメソッドを呼び出す必要があるので,this.$emit構文を使って親に値を渡します.

doneEdit: function () {
  var utc = new Date(this.thedate)
  utc.setHours(utc.getHours() + 9)
  var jstStr = utc.getFullYear() + '-' + ('00' + (utc.getMonth() + 1)).slice(-2) + '-' + ('00' + utc.getDate()).slice(-2)

  this.$emit('set-date', { date: jstStr })
}

$emitに書かれた set-date は親のApp.vueの子コンポーネントタグ内に書かれている @set-date=”setDate” です. つまり,子側からset-dateで呼ばれた親はそれを受けて自分(つまり親)のsetDateメソッドを呼出しているわけです.

setDate: function (data) {
  this.activity.date = data.date
}

こんな感じで親のactivitiy.dateに子から渡された値をセットしています.

JavaScriptのnew DateはどうもTimeZoneを持てない?ようで値がUTCで戻ってきてしまいます.そこで,これはズルなのですが+9時間してJSTの文字列に変換してます. かなり怪しげなコードですが,これは目をつぶってください. 正しくは Moment.js 等を使って対応するのが望ましいはずです.

Vue.jsを使い始めた当初,この一連の関係を理解するまでに少し時間が掛かりましたが,一通り理解出来れば後は個別に書いていけば良いのでかなり楽でした.

しかし,上のサンプルは日付を日付型としてみたり文字列で返してみたりと型が一貫していないのでお恥ずかしいし,直すべきですね. 上述中にも数カ所「どうもきちんと理解しきれていない」箇所があるので,いずれ時間のある時に突き詰めておきます. 細かい点もあるんですが,バグってたいていこういう細かい点が原因で発生するんですよね.( ;∀;)

追記:
上で moment.js を紹介してますが,どうも怪しい部分があるようで,最近は date-fnsを利用されるのが良い様です. 自分はどちらも使ったことが無いので,とりあえず後からMoment.jsの課題に対処すべく出てきた date-fns を使ってみようと思ってます.

投稿者:

shun

あいかわらずのプログラマ人生 ・・・なんだけど、徹夜をすることが出来なくなりました。 肩を壊してしまい,リハビリに通ってます.( ;∀;)

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です