こちら、Vue.js Advent calendar #3 13日目の記事です〜。
13日過ぎてますね、ゴメンナサイ。仕事テンパってました。。
ごあいさつ的な
ソニックムーブっていう会社で、マークアップしたりJavascriptをちょいちょい触ったりしているココエっていいます。
今年10月に、弊社でcomsbiという、LINE@・LINEビジネスコネクトを利用した、ソリューションサービスをリリースしました。
comsbiには、管理画面からLINEメッセージ送信をする機能がありまして(行動データを元にしたセグメント配信とかできます)、
そのメッセージ送信機能のUIを作っている時に、はまったことと、どう解決したかを書きたいと思いますー。
TL;DR
v-forで、input[type=file]を含むコンポーネントをループさせて、かつ順番入れ替えする要件があるなら、CSS flexboxの order
プロパティ を使うと解決できます。
改修前のNGケース
スマホ・プレビュー画像の右に、入力UI(テキスト、画像)がありますが、ここを、任意の配列に基づいて v-for
でコンポーネントをループさせています。
全コードを掲載できないので、JSFiddleでどんな構造なのか再現してみました。
https://jsfiddle.net/kokoe/5vsx83ym/
問題点
入力UIの機能に ↑
と ↓
というボタンがあり、このボタンをクリックすると、上下UIの順番を入れ替えることができます。
ファイルを選択して、順番入れ替えすると、(プレビュー画像は更新されるものの)input[type=file]のデータが更新されなかったり、テキストと画像を入れ替えすると、ファイル選択済みのinput[type=file]がリセットされていることがわかります。
順番入れ替えは、配列要素の入れ替えで実装しています。
new Vue({
methods: {
movePrev(index) {
let prevIndex = index - 1;
// ↓配列要素の入れ替え
this.messages.splice(prevIndex, 2, this.messages[index], this.messages[prevIndex]);
}
}
}
原因
ごめんなさい、原因を調べられていないのですが、仮想DOMと差分レンダリングの仕組みあたりが原因なのかなと推測しています。
(間違ってたら、突っ込みください。。)
2018/12/19追記
yuta0801さんより、コメントでご指摘いただきましたが、
v-forする際、keyにユニーク値を指定していなかったことが原因のようです。(NGケースだと、keyにインデックス値を利用しているためユニークでない)
<-- NG v-bind:keyにインデックスを指定している -->
<component
v-for="(message, index) in messages"
v-bind:is=" 'message-ui-' + message.type "
v-bind:key="index"
v-bind:index="index"
v-bind:data="message"
v-bind:disabled-prev="index === 0"
v-bind:disabled-next="index === (messages.length - 1)"></component>
yuta0801さん、ありがとうございましたm(_ _)m
解決方法
2018/12/19追記
(あとで、ちゃんと書き直しますが)
v-forのkeyにユニーク値を指定すれば解決します。
以下は、順番入れ替えを、配列要素の入れ替えで実装しない場合のやり方で、
自分が対応した方法の1例となります。
順番入れ替えを、配列要素の入れ替えで実装していましたが、
order
という順番を管理するプロパティをもたせて、CSS flexboxの order
プロパティで順番入れ替えをするように実装したところ、入れ替えを行ってもinput[type=file]のデータが保持されるようになりました。
先ほどの、JSFiddleを order
を使って修正したコードがこちらです
https://jsfiddle.net/kokoe/vj7re95L/
あとがき的な
- CSS flexboxの
order
プロパティって、どこで使うんじゃ。。ってずっと思ってたんですが、まさかこんなところで役にたってくれると思いませんでしたw - 解決できなかった点としては、orderで入れ替えした際、アニメーションつけたかったのですができませんでした。時間ができた時にでも調べたいと思います。
- 解決方法ですが、input[type=file]の、ファイルがセットされた時点で、ajaxでファイルあげちゃうとかで解決できるなら、それでもいいのかなって思います。
どなたかのお役に立てればこれ幸い。ではでは。