JSerがElmとFirebase使ってみた

業務ではABテストなどでVanilla JSばかり書いていた私がElmに興味を持ったのでElmとFirebaseを連携させたアプリを作成してみました。

Elmはバックエンド出身の人がフロントを書くために使うというパターンが多いような気がしていますが、私のようなフロントの人間がElmを使ってみたというのも参考になるかと思い投稿しました。

初めての投稿なのでお手柔らかにお願いします。

<修正、追記>

2019.01.04 Firebaseからのデータ設定・取得にsubscritiontoHex関数、

@ababup1192さん、@miyamo_madokaさん、ありがとうございます


Elmとは

https://elm-lang.org/

haskellで書かれた関数型altJSです。haskellに似ている部分が多くあるものの結構な部分が削ぎ落とされており文法がシンプルなので自分のようなフロントエンド出身の人間でも扱いやすいと感じました。

(ver 0.19)


作成したアプリについて

g.gif

クリックするとランダムで背景色が変わるというものです。これではわかりづらいですがランダム値はFirebaseに保存されています。


主な目的


  • Elmに慣れること

  • ElmとFirebaseの連携


Elmを始める&ドキュメント

「2019年 Elmをはじめる人が最初に読むページ」

https://qiita.com/arowM/items/5ec5853298fc880353b7

まずはこれを読んで「公式ドキュメント(Elm Guide)」がいかに重要なのかを知っておきます。その上で以下の内容を読むことで知識を深めていきます。

<公式ドキュメント>

https://guide.elm-lang.org/

<公式ドキュメント:翻訳版>

英語が苦手なら有志の方々が翻訳している日本語版もある

https://guide.elm-lang.jp/

※一部未翻訳部分もあるので、そういうところはGoogle翻訳とかで。

<基本文法の解説:Elm入門ハンズオン>

https://gitpitch.com/ababup1192/elm-handson2/master?grs=github&t=sky

by @ababup1192さん

 

自分の場合はさらに悩みをツイートしていたら @miyamo_madokaさん、@ababup1192さんに拾ってもらったので疑問点を聞いたりしていました。


主な機能や悩みどころ


ランダム値から16進数への変換

https://package.elm-lang.org/packages/rtfeldman/elm-hex/latest/

これを使いました。

elm install rtfeldman/elm-hex

パッケージのインストールをします。

   

import Hex exposing (..)

importして読み込ませます。

 

toHex : Int -> String

toHex num =
num |> Hex.toString |> String.padLeft 6 '0'

padLeftというゼロパディングを簡単にできる便利関数を使用しました。


JSとのやりとり

JSとやりとりをする方法は以下の2つがあります。


  • flags

  • ports

flagsは初期化時に処理を加えるもので、今回のような

ランダム値を作成→JSに送る→Firebaseに保存

というものには適さないのでportsを使用しました。

portsについては公式ドキュメントに書かれているものの、型定義までで詳しい使い方のサンプルコードはないので以下の記事を参考にしました(ちょっと古いですが)。

https://qiita.com/boiyaa/items/c5670817f17938b7a755


Firebaseからのデータ設定・取得

https://firebase.google.com/docs/firestore/manage-data/add-data

https://firebase.google.com/docs/firestore/query-data/get-data

https://firebase.google.com/docs/firestore/query-data/listen

データ設定・取得はこれらを参考にしました。


  • コレクション:elm

  • ドキュメント:txt

という名前にしています。


ElmからJS

const docRef = firestore.collection("elm").doc('txt');

//Elm to JS
app.ports.toJs.subscribe(function(doc) {
docRef.set({
data: doc
});
});


ElmからJSにデータを流します。subscriptionsについては以下のように設定しました。


方法1:Portsでのsubscriptions

Javascript側


JSからElm

//JS to Elm 

docRef.onSnapshot(function(doc) {
app.ports.fromJs.send(doc.data().data)
}, function(error) {
console.log(error)
});

Firebaseからの読み込みが完了したときに app.ports.fromJs.send でDB内のデータをElmに送信します。get ではなく onSnapshot を使用しているのは onSnapshot だとリアルタイム更新をしてくれるからです。

※ブラウザを複数開いていればカラーコードの変更がリロードなしで反映されます。

Elm側


定義

port fromJs : (Int -> msg) -> Sub msg


Firebaseには数値を保存しているのでこのように定義します。

 


ElmからJS

type Msg

= GetData Int

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
GetData num ->
( { model
| randomNumber = num
, stat = Success
}
, Cmd.none
)

subscriptions : Model -> Sub Msg
subscriptions model =
fromJs GetData


subscriptionsで読み込ませます。

Firebaseからの読み込みが完了したときにデータをElmに送信し、そのデータをもとにviewが変更されます。


方法2:JsonをつかってREST読み込み

注 この方法はリアルタイムアップデートに向いていません

https://firebase.google.com/docs/firestore/use-rest-api

取得方法はここに書いてあるように取得します。

https://firestore.googleapis.com/v1beta1/projects/【プロジェクト名】/databases/(default)/documents/elm/txt  


Typeとtype alias

公式ドキュメントにある Json と Random で Model の書き方が違っているため組み合わせた方法を知りたくなりました。


Json

type Model

= Failure
| Loading
| Success String


Random

type alias Model =

{ dieFace : Int
}

https://guide.elm-lang.org/types/custom_types.html

に組み合わせた書き方が書かれていたので参考にしました。

さすが公式ドキュメント!

type Stat

= Loading
| Success
| Failure

type alias Model =
{ randomNumber : Int
, stat : Stat
}

最終的にはこのようにして落ち着きました。


振り返り


  • 迷ったらとにかく公式ドキュメント。大抵の悩みはこれを見ればだいたい解ける!

  • それでも困ったらツイッターやdiscordで聞こう。すごいエンジニアさんがたくさんいます。

  • あと必要なのは英語力。でもGoogle翻訳と英文をにらめっこするのもアリ。でもGoogle翻訳は誤訳もあるので・・・


感想

Elmは前提として関数型の使い方とElm Architectureという独特なフレームワークに慣れないといけないという問題がありますが、慣れてくれば非常にやりやすいと感じました。

一番はなんといっても「No runtime errors」。基本的にランタイムエラーが無く、起きたとしてもJS側で読み込みさせるとかのJS連携させている場合のみです。なのでコンパイルさえ通れば安心して使えるというのが強みです。エラーがなければそれだけ調査とかに使う時間も減らすことができます。

今回はFirebaseとの簡単な連携をさせました。Elmでできる処理は全てElmにまわしているのでFirebase側の処理が最小限でできました。


Firebaseは非常に強力で便利なのですが非同期での取得が基本なので複雑になりがちです。JSだけでタスク管理ツールを作成したことがありますが、データ処理が複雑になりすぎて途中で改良を加えようとしたら全面的に修正を施す必要が出てきて心が折れたことがあります。

Elmはこういう複雑極まりないJSだけのアプリケーションの複雑度を下げるアプリケーションとして非常に有用なのではないかと思いました。最終的にはJSに吐き出すので当然JSでどのような動きをさせることができるかという知見は今までと同様に使えますし、基本文法も他の関数型プログラミング言語に比べれば簡単なので使ってみてはいかがでしょうか。