LoginSignup
1
1

More than 1 year has passed since last update.

【Elm】Firebase Firestoreにデータを追加する

Last updated at Posted at 2021-07-24

はじめに

前の記事でElmでFirebase Authする方法を書いたので、その流れでFirestoreにデータを追加する方法を書きます。
基本的にやってること同じなので前と似たようなことしか書いてないです(なんで書いた?)
間違ったこと書いてたら罵詈雑言でも構いませんので教えてください。

やりたいこと

Google SignIn後に、フォームにTitleとContentsを入力して、Saveボタンを押すと、Firestoreに文字列を追加します。
output.gif

以下がFirestoreの画面です。ちゃっかり追加した時刻も入れています。
スクリーンショット 2021-07-23 17.57.00.png

開発環境

Mac
create-elm-app使用

コード全体

こちら参照ください
index.jsには各自のFirebaseのapiKey等を記載してください。

大まかな流れ

  1. Firebase FireStoreの初期設定
  2. Elm側でコンテンツ入力フォームを作る
  3. フォームに入力されたコンテンツ情報をJavaScriptに送る
  4. JavaScriptでコンテンツ情報を受け取り、Firestoreに追加する
  5. 追加結果をElmで受け取る

1. Firebase FireStoreの初期設定

ここでは特に説明しません。ググればすぐに出てきます。この記事とか良さそうです(適当)

2. Elm側でコンテンツ入力フォームを作る

入力フォームの書き方は前の記事の「2. Elm側でemail、パスワード入力フォームを作る」で書いたので省略します
←めんどくさいだけ

3. フォームに入力されたコンテンツ情報をJavaScriptに送る

PORT

フォームに入力したコンテンツ情報を、portを使ってJavaScriptに送り、JavaScriptでfirestoreに追加します。
portに関しての詳しい解説は公式ドキュメントや他の方の記事を読むと良いと思います。

port saveContents : Encode.Value -> Cmd msg

UPDATE

例によってコンテンツ情報をJavaScriptに送るためには、"エンコード"する必要があります。
エンコードに関しての詳しい解説は公式ドキュメントや他の方の記事を読むと良いと思います。

update : Msg -> Model -> ( Model, Cmd Msg )
update msg =
    case msg of
        ...
        SaveMsg saveMsg ->
            updateSaveContent saveMsg
        ...

-- エンコードした文字列をportに引数として渡す
updateSaveContent : SaveMsg -> Model -> ( Model, Cmd Msg )
updateSaveContent msg model =
    case msg of
        SaveContent ->
            ( model, saveContents <| contentsInfoEncoder model )

-- titleとcontentのエンコード
contentsInfoEncoder : Model -> Encode.Value
contentsInfoEncoder model =
    Encode.object
        [ ( "title", Encode.string model.contents.title )
        , ( "content", Encode.string model.contents.content )
        ]

VIEW

コンテンツをFirestoreに追加するSaveボタンも作っておきます。


view : Model -> Html Msg
view model =
    div []
        [ ...
        , Html.map SaveMsg viewSaveButton
        ...
        ]

viewSaveButton : Html SaveMsg
viewSaveButton =
    div []
        [ button [ onClick SaveContent ] [ text "Save" ] ]

4. JavaScriptでコンテンツ情報を受け取り、Firestoreに追加する

初期設定

JavaScript側の初期設定です。
自分はcreat-elm-appを使っているので、appの定義とかは皆さんと異なるかもしれません。
とりあえずappの定義は忘れずにやってください。
firebase関係のコードは基本的に同じになると思います。

...
import firebase from "firebase/app";
import "firebase/analytics";
import "firebase/auth";
import "firebase/firestore";

// ここが異なるかも
const app = Elm.Main.init({
  node: document.getElementById("root"),
});

const firebaseConfig = {
  // 各自のapi key等を記載してください
};

// firebaseの初期化
firebase.initializeApp(firebaseConfig);
const Googleprovider = new firebase.auth.GoogleAuthProvider();
const DB = firebase.firestore();

コンテンツ情報を受け取り、Firestoreに追加する

こちらの公式ドキュメントに従って、Firestoreにデータを追加します。

portを使用してJavaScriptとデータのやり取りをしたい場合、以下のように、app.ports.(Elm側で記載したポート名).subscribe...というように記載します。

app.ports.saveContents.subscribe((Contents) => {
  //現在ログインしているユーザー
  const user = firebase.auth().currentUser;
  //サーバーの時間
  const time = firebase.firestore.FieldValue.serverTimestamp();
  //コンテンツを追加する参照先(.doc()でidを自動生成)
  const ref = DB.collection("Test").doc(user.uid).collection("Contents").doc();
  ref
    //コンテンツの追加を行う
    .set(
      {
        //Elm側でエンコードしたtitle, content
        Title: Contents.title,
        Content: Contents.content,
        //データ追加時のサーバーの時間
        timestamp: time,
      },
      //コンテンツを上書きするかしないかの指定。Trueにすると上書きせず、ドキュメントにコンテンツが新規追加されていく。
      { merge: true }
    )
    //追加に成功したらPass, 失敗したらFailをElmに返す
    .then(() => {
      app.ports.validateFirestore.send("Pass");
    })
    .catch(() => {
      app.ports.validateFirestore.send("Fail");
    });
});

5. 追加結果をElmで受け取る

PORT

JavaScriptから認証状態の文字列を受け取る用のportsを用意します。
文字列を受け取るので(String -> msg)にしています。

port validateFirestore : (String -> msg) -> Sub msg

MODEL

追加の状態を表すカスタム型を定義します。

type FirestoreState
    = Pass
    | Fail

type alias Model =
    { ...
    , firestoreState : FirestoreState
    ...
    }

SUBSCRIPTIONS

portで受け取ったデータをElmのUPDATEで使用するために、SUBSCRIPTIONに以下のように記載します。
カスタム型を使用しているので、Sub.mapを使用しています。
また以下のように複数のSbuscriptionを書きたいときは、Sub.batchを使用します。

subscriptions : Model -> Sub Msg
subscriptions model =
    Sub.batch
        [ validateAuthState ValidateAuthState
            |> Sub.map ValidateAuthStateMsg
        , validateFirestore ValidateFirestore
            |> Sub.map ValidateFirestoreMsg
        ]

UPDATE

JavaScriptから受け取った文字列に応じて、firebaseStateを更新します。

type ValidateFirestoreMsg
    = ValidateFirestore String

type Msg
    = ...
    | ValidateFirestoreMsg ValidateFirestoreMsg

update : Msg -> Model -> ( Model, Cmd Msg )
update msg =
    case msg of
        ...
        ValidateFirestoreMsg validateSaveContentsMsg ->
            updateValidateFirestoreState validateSaveContentsMsg

--受け取った文字列に応じてfirestoreStateを更新する
updateValidateFirestoreState : ValidateFirestoreMsg -> Model -> ( Model, Cmd Msg )
updateValidateFirestoreState msg model =
    case msg of
        ValidateFirestore contentsmsg ->
            case contentsmsg of
                "Fail" ->
                    ( { model | firestoreState = Fail }
                    , Cmd.none
                    )

                "Pass" ->
                    ( { model | firestoreState = Pass }
                    , Cmd.none
                    )

                _ ->
                    ( model, Cmd.none )

VIEW

データ追加の結果を表示します。
firebaseStateに応じて表示する文字列を変更しています。

view : Model -> Html Msg
view model =
    div []
        [ ...
         , viewValidateFirestore model
        ]

viewValidateFirestore : Model -> Html Msg
viewValidateFirestore model =
    case model.firestoreState of
        Pass ->
            div []
                [ div [] [ text "Firestore Status: Pass" ]
                ]
        Fail ->
            div []
                [ div [] [ text "Firestore Status: Fail" ] ]

以上でできました!

参考文献

・Qiitaに投稿してくださった記事(参考になります本当にありがとうございます!!)
 Elm portsでFirebase Firestoreを触ろう!

・Firebase系
 Firebase Firestoreの初期設定
 Firestoreへのデータの追加

・Elm公式ドキュメント
 型エイリアス
 カスタム型
 ポート
 Json.Encode
 Platform.Sub

・関連する自分の記事
 【Elm】Firebase Authenticationでパスワード認証をする

1
1
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
1
1