前回は、施設情報を編集してモデルを更新するところまで作りました。
今回は編集内容をデータベースに反映させる処理です。
このコミットです。
コミット単位で記事を書く方法、良いですね。この記事を読んでくれている人は今の所誰もいないかもしれませんが、コミット内容の説明になるので、あとで自分で見返す時にもわかりやすいです。githubにはこの記事のリンクを貼っていないから辿れませんが。
流れとしては、
施設情報の編集
↓
“save”ボタン
↓
データベースに反映するためのPATCHリクエスト
↓
成功したらモデルをアップデート、失敗したらそのまま
↓
ビュー
となります。以下の図のような流れです(公式チュートリアルの図なので関数名は全然違います)。
ポイントは、この一連の処理でメッセージを2回使っているところです。1回目は、save -> リクエストのところです。2回目は、リクエスト結果 -> モデルアップデートのところです。データベースへの反映がなんらかの理由で失敗したときにモデルをアップデートしないようにするためです。ただ実は、今回は意味のない仕様になっています。なぜなら各フィールドの値を変更するたびにモデルをアップデートしているからです。全部のフィールドの情報を編集してから、最後に一回だけリクエストを送るようにしたかったのでこのようにしました。もし、入力するたびにDBへの反映まで行うなら、上記の仕様は意味を持ちます。
Httpリクエストを送るためのコマンドです。saveボタンを押してメッセージが送信されると、saveFacilityCmd
コマンドが発行されます。このコマンドは、DBに反映するためのsaveFacilityRequest
を送り、リクエスト結果をOnFacilitySave
メッセージに乗せます。
-- FacilityデータをJSONにエンコードするための追加インポート
import Json.Encode as Encode
-- FacilityをJSONにエンコード
facilityEncoder : Facility -> Encode.Value
facilityEncoder facility =
let
attributes =
[ ("id", Encode.string facility.id)
, ("name", Encode.string facility.name)
, ("opening", openingEncoder facility.opening)
, ("address", Encode.string facility.address)
, ("postcode", Encode.string facility.postcode)
, ("web_site", Encode.string facility.web_site)
, ("description", Encode.string facility.description)
]
in
Encode.object attributes
openingEncoder : Opening -> Encode.Value
openingEncoder opening =
let
attributes =
[ ("open", Encode.string opening.open)
, ("close", Encode.string opening.close)
]
in
Encode.object attributes
-- src/Update.elmから呼ばれるコマンド
saveFacilityCmd : Facility -> Cmd Msg
saveFacilityCmd facility =
saveFacilityRequest facility
|> Http.send Msgs.OnFacilitySave
-- 編集したFacilityデータをDBに反映するためのPATCHリクエスト
saveFacilityRequest : Facility -> Http.Request Facility
saveFacilityRequest facility =
Http.request
{ body = facilityEncoder facility |> Http.jsonBody
, expect = Http.expectJson facilityDecoder
, headers = []
, method = "PATCH"
, timeout = Nothing
, url = saveFacilityUrl facility.id
, withCredentials = False
}
-- リクエストURL
saveFacilityUrl : FacilityId -> String
saveFacilityUrl facilityId =
fetchFacilitiesUrl ++ "/" ++ facilityId
src/Facilities/SingleEdit.elmの編集
saveボタンを押した時に、SaveUpdatedFacility
メッセージが送信されるようにします。
-- 変更前(editForm関数の一部)
, button [] [ text "Submit"]
-- 変更後
, button [onClick (Msgs.SaveUpdatedFacility model)] [ text "Save"]
src/Msgs.elmの編集
DB反映リクエスト用のメッセージとその後のモデルアップデート用メッセージを定義します。
-- 追加インポート
import Http
-- メッセージを2つ追加
| SaveUpdatedFacility Facility
| OnFacilitySave (Result Http.Error Facility)
-- 追加インポート
import Commands exposing (saveFacilityCmd)
-- 定義したメッセージに対応するアップデート処理を追加
-- リクエスト結果を受けるときは、OkとErrでそれぞれ定義
Msgs.SaveUpdatedFacility facility ->
( model, saveFacilityCmd facility)
Msgs.OnFacilitySave (Ok facility) ->
( updateFacility model facility, Cmd.none )
Msgs.OnFacilitySave (Err error) ->
let _ = Debug.log "OnFacilitySave error:" error in
( model, Cmd.none )
以上で、DB反映処理ができました。ちなみに、この編集ページは今こんな感じになっています。
CSSを書いていないので、とてもつまらない画面です。basscssを使えるようにしているので、自分で書かなくてもクラスを書けば色々やってくれるはずです。そしてこの記事のコミット時点では、saveボタンもまだcss適用前の味気ないボタンですので。
今の状態では、saveボタンを押した時にちゃんと保存(DB反映)されたのかどうかがわからないため、次回はそのステータスを表示するようにしていきます。