【Vue】【Rails】selectタグのv-modelとActive Hashの更新
はじめに
こんにちは!大ちゃんの駆け出し技術ブログです。
Active Hashを使用して出身地を登録するフォームを作っていたのですが、そこで少しつまずいたので備忘録として残しておきます。
つまづいた箇所
以下のようにActive Hashを使用して県名を登録しています。
class Prefecture < ActiveHash::Base self.data = [ { id: 1, name: '北海道' }, { id: 2, name: '青森県' }, { id: 3, name: '岩手県' }, { id: 4, name: '宮城県' }, { id: 5, name: '秋田県' }, { id: 6, name: '山形県' }, { id: 7, name: '福島県' }, { id: 8, name: '茨城県' }, { id: 9, name: '栃木県' }, { id: 10, name: '群馬県' }, { id: 11, name: '埼玉県' }, { id: 12, name: '千葉県' }, { id: 13, name: '東京都' }, { id: 14, name: '神奈川県' }, { id: 15, name: '新潟県' }, { id: 16, name: '富山県' }, { id: 17, name: '石川県' }, { id: 18, name: '福井県' }, { id: 19, name: '山梨県' }, { id: 20, name: '長野県' }, { id: 21, name: '岐阜県' }, { id: 22, name: '静岡県' }, { id: 23, name: '愛知県' }, { id: 24, name: '三重県' }, { id: 25, name: '滋賀県' }, { id: 26, name: '京都府' }, { id: 27, name: '大阪府' }, { id: 28, name: '兵庫県' }, { id: 29, name: '奈良県' }, { id: 30, name: '和歌山県' }, { id: 31, name: '鳥取県' }, { id: 32, name: '島根県' }, { id: 33, name: '岡山県' }, { id: 34, name: '広島県' }, { id: 35, name: '山口県' }, { id: 36, name: '徳島県' }, { id: 37, name: '香川県' }, { id: 38, name: '愛媛県' }, { id: 39, name: '高知県' }, { id: 40, name: '福岡県' }, { id: 41, name: '佐賀県' }, { id: 42, name: '長崎県' }, { id: 43, name: '熊本県' }, { id: 44, name: '大分県' }, { id: 45, name: '宮崎県' }, { id: 46, name: '鹿児島県' }, { id: 47, name: '沖縄県' } ] end
コントローラーはprefecture_id
をパラメーターとして受け取り登録しています。
def create @profile = current_user.build_profile(profile_params) if @profile.save render json: @profile else render json: @profile.errors, status: :bad_request end end private def profile_params params.require(:profile).permit(:height, :gender, :blood_type, :prefecture_id) end
ここで、フロント側に渡すJSONの値として、prefecture_id
をそのまま渡してしまうと、Active Hashで定義しているid
が返ります。例えば、北海道ならprefecture_id
が1と返ってきます。フロント側では県名を表示したいので、ActiveModelSerializersを使用して、prefecture.name
とすることで県名の値(idが1なら北海道)を返却します。
# app/serializers/profile_serializer.rb class ProfileSerializer < ActiveModel::Serializer attributes :id,:height, :gender, :blood_type, :prefecture_id belongs_to :user def prefecture_id object.prefecture.name end end
このActiveModelSerializersの箇所は過去記事で説明しているのでよければ参照ください。
さて、詰まった箇所は以下の部分です。
県名の編集のためにvue側でも同じようにprefectures
で定義し、それを<template>
タグ内で繰り返し処理でoption
に展開していました。
data() { return { prefectures: [ { text: "北海道", value: "1" }, { text: "青森県", value: "2" }, { text: "岩手県", value: "3" }, { text: "宮城県", value: "4" }, { text: "秋田県", value: "5" }, { text: "山形県", value: "6" }, { text: "福島県", value: "7" }, { text: "茨城県", value: "8" }, { text: "栃木県", value: "9" }, { text: "群馬県", value: "10" }, { text: "埼玉県", value: "11" }, { text: "千葉県", value: "12" }, { text: "東京都", value: "13" }, { text: "神奈川県", value: "14" }, { text: "新潟県", value: "15" }, { text: "富山県", value: "16" }, { text: "石川県", value: "17" }, { text: "福井県", value: "18" }, { text: "山梨県", value: "19" }, { text: "長野県", value: "20" }, { text: "岐阜県", value: "21" }, { text: "静岡県", value: "22" }, { text: "愛知県", value: "23" }, { text: "三重県", value: "24" }, { text: "滋賀県", value: "25" }, { text: "京都府", value: "26" }, { text: "大阪府", value: "27" }, { text: "兵庫県", value: "28" }, { text: "奈良県", value: "29" }, { text: "和歌山県", value: "30" }, { text: "鳥取県", value: "31" }, { text: "島根県", value: "32" }, { text: "岡山県", value: "33" }, { text: "広島県", value: "34" }, { text: "山口県", value: "35" }, { text: "徳島県", value: "36" }, { text: "香川県", value: "37" }, { text: "愛媛県", value: "38" }, { text: "高知県", value: "39" }, { text: "福岡県", value: "40" }, { text: "佐賀県", value: "41" }, { text: "長崎県", value: "42" }, { text: "熊本県", value: "43" }, { text: "大分県", value: "44" }, { text: "宮崎県", value: "45" }, { text: "鹿児島県", value: "46" }, { text: "沖縄県", value: "47" }, ],
<template>
タグ内で繰り返し処理で展開
<select v-model="editProfile.prefecture_id" name="profile[prefecture_id]" > <option v-for="prefecture in prefectures" :key="prefecture.value" :value="prefecture.value" > {{ prefecture.text }} </option> </select>
:value="prefecture.value"
とすることでサーバー側に渡すパラメータはid
で渡すようにしています。これはActive Hashで更新するためのパラメータがid
であるためです。
※この部分については少し冗長かなと思ったので、のちほど改善したいと思っています。例えば、Active Hashで登録している値をAPIレスポンスで受け取りそれをフロント側で表示することで、サーバー側のActive Hashの値とフロント側の値を同じにすることができます。今だと、フロント側とサーバー側で別々で県名を指定しているので冗長かなと思います。
ここで問題となったのがv-model
の値はoption
の中のvalue
と一致しないと編集画面を表示したときに、何も選択されていない状態で表示されてしまうことです。
editProfile.prefecture_id
は上述したように県名を表示するために、値はid
ではなく県名となっています。しかし、select
タグ内ではvalue
はid
となっているために県名がどのoptionのvalueにも当てはまらない状態です。そのため、editProfile.prefecture_id
には値が格納されていますが、図のようにフォームに何も選択されていないような状態で表示されてしまうのです。
解決策
ベストプラクティスかは不明なのですが、option
のvalue
を県名と一致させる方法を取りました。つまり、:value="prefecture.text"
とすることで、value
が県名になるようにしました。
<option v-for="prefecture in prefectures" :key="prefecture.value" :value="prefecture.text" > {{ prefecture.text }} </option>
これで編集画面を開いたときに、value
とv-model
の値が一致するために初期状態で値が選択されているようになります。
しかし、これだとサーバー側で受け取るパラメーターとしてはid
ではなく県名になってしまい、正常に値を更新できなくなってしまいます。そこで、更新するときにパラメータを変更するようにしました。
const selectedPrefecture = this.prefectures.find( (prefecture) => prefecture.text == editBasicProfile.prefecture_id ); editBasicProfile.prefecture_id = selectedPrefecture.value;
data
で登録している県名の中からパラメーターとして渡っているeditBasicProfile.prefecture_id
と一致する県名のオブジェクトを取得します。そして、そのオブジェクトに格納されているvalue(id)
を代入することで値をid
に無理やり変更しています。
これにより無事値を更新することができました!