Upgrade to Pro — share decks privately, control downloads, hide ads and more …

始めようElmでフロント開発_その02_ローカルWebアプリ

taashi
April 25, 2019

 始めようElmでフロント開発_その02_ローカルWebアプリ

社内勉強会

taashi

April 25, 2019
Tweet

More Decks by taashi

Other Decks in Programming

Transcript

  1. App ▪MVCって? Model View Controller User 使用 表示 操作 更新

    構成部分 ・Model データ、値を保持 ・View UI、情報の表示方法 ・Controller 入力によるmodelの変換 UIを持つアプリケーションの実装デザインパターン
  2. ▪The Elm Architectureって? ・ シンプル ・ モジュール化しやすい ・ 再利用しやすい ・

    テストしやすい etc ・ リファクタリングしやすい ElmのWebアプリケーションを構築するためのパターン 元々、初期のElm使いがよく使っていた 構造パターンを採用した!
  3. ・基本的な構成部分 ・ view (関数) webとして状態を表示する方法 ・ update (関数) 状態(Model)の更新方法 ・

    Model (値) アプリケーションの状態 状態(Model)をweb表現に変換する関数 状態(Model)を新たな状態に変換する関数 メイン要素 サブ要素 ・ Msg (値) 更新の種類(更新指示) 必要な情報をまとめたタイプエイリアス 種別をバリアントとするカスタムタイプ
  4. ・MVCに当てはめたイメージ App Model (値) view (関数) update (関数) User 使用

    表示 新しいモデル 更新 Msgによって 操作を分岐 似ている...! Msg (値)
  5. まずは、プロジェクトの作成と、初期化を行います。 実行時に出てきた質問は、[Y]を入力してEnterしてください。 以下のものが作成されていたら、完了です。 ・ elm.json ・ src/ (中身が空) $ elm

    init ※ この操作は、新規のElm開発の度に、必要となる操作です。 を実行します。 次に、メインソースの作成を行います。 src/ 以下に、”Main.elm”という名前で、elmファイルを作成し、エディタで開きましょう。 オススメエディタ(+拡張機能) : VSCode (+ elm) ・開発の準備 コンソールを開き、本日作業したいディレクトリ(空のディレクトリをおすすめします。)に移動し、
  6. ・基本的な構成部分の記述(全体像) -- Model type alias Model = { ~省略~ }

    -- Msg type Msg = ~ 省略 ~ -- Update update : Msg -> Model -> Model update msg model = ~ 省略 ~ -- View view : Model -> Html Msg view model = ~ 省略 ~ 基本構成部分の記述は、 ~省略~ 部分にそれぞれ内容や処理を書いていく。 ただし、これは、構成部分のみであり、 実際には、これを取りまとめる部分が必要となる... 後で、それぞれ取り上げるので、 ここでは、一旦眺めるだけにしておきましょう。 ←こんな感じ
  7. ・構成部分をまとめる処理の記述 import Browser main : Program () Model Msg main

    = Browser.sandbox { init = init , update = update , view = view } 構成部分をまとめる処理(アプリケーションのmain)は、Browserモジュールを使用します。 いくつかの書き方がありますが、今回は、一番シンプルな形式である”sandbox”を使用しましょう。 記述方法は、以下のとおりです。 sandboxは、一番シンプルな「基本構成のみで完結するwebアプリケーション」用のmainを生成してくれます。 初期化したModelを渡す関数(ここでは、init)をinitフィールドに渡す。 基本構成部分であるview関数をviewフィールドに渡す。 基本構成部分であるupdate関数をupdateフィールドに渡す。
  8. ・値の有無を保持する ここで、今後必要になる言語機能として、値の有無を保持する方法を紹介します。 値だけでなく、値があるかないかも保持したい場合があります。 この様な場合は、Maybe型を使用すると、簡単に実現できます。 type Maybe a = Just a

    | Nothing Maybe型は、カスタムタイプとして、右のように定義されています。 値があるときは、Just 。無いときは、Nothingとして扱えます。 >data = Just 1.1 Just 1.1 : Maybe Float >data = Nothing Nothing : Maybe a ・値に拘束する際は、 以下のように拘束します。 case data of Just f -> -- 値が有るときの処理 Nothing -> -- 値が無いときの処理 ・値を使う際は、以下のようにcase文で、 パターンマッチさせて使います。
  9. ・Modelの記述 -- Model type alias Model = { leftTerm :

    Maybe Float , rightTerm : Maybe Float , answer : Maybe Float } “Model”は、そのアプリケーションの状態を表す値の型です。 大体の場合、アプリケーションの状態を表す値が、複数必要なので、レコード型になります。 レコード型をそのまま扱うと大変なので、タイプエイリアスとして、Model型を定義します。 上は、あくまで型の定義です。 アプリケーションでは、このModel型の値を生成し、使用したいため、 初期値を入れたModel型の値を返す関数も定義しておく必要があります。 この様な関数は、一般にinit関数として、右のように定義します。 今回は、2つの項と答えを保持したいので、下のように記述しましょう。 init : Model init = { leftTerm = Nothing , rightTerm = Nothing , answer = Nothing } または、 init : Model init = Model Nothing Nothing Nothing
  10. ・Msgの記述 -- Msg type Msg = SetLeftTerm (Maybe Float) |

    SetRightTerm (Maybe Float) | Addition | Subtraction “Msg”は、状態の更新の種類(更新指示)をまとめた型です。 大体の場合、更新指示は、複数種類必要で、かつ、まとめたときは、それぞれを同列で扱いたいので、 カスタムタイプとして、Msg型を定義します。 ・左項の設定 ・右項の設定 ・足し算 ・引き算 今回は、以下(左側)の4つの更新指示が必要なので、以下(右側)のように記述しましょう。 となる。 項の設定は、 設定する値も必要なので、関連データとして、 その値の型を後ろに添える 計算処理は、 Modelで保持している値で行うので、 その他の値を添えなくて良い その更新指示の引数みたいなイメージ! ちなみに、 > SetLeftTerm (Just 1.2) SetLeftTerm (Just 1.2) : Msg > SetLeftTerm <function> : Maybe Float -> Msg
  11. ・レコード型の値の一部更新 ここで、今後必要になる言語機能として、レコード型の値の一部更新の仕方を紹介します。 タイトルには、「更新」と書いたが、正確には、Elmの値はイミュータブルなので、 recodeAの値が更新されたのではなく、 “b”フィールドのみ’12‘で、それ以外はrecodeAと同一の値をもつ、新しいレコード型の値が返ってきて(recodeBとし て拘束して)いる。 > recodeA = {

    a = 1.0, b = 2.0, c = 3.0 } { a = 1.0, b = 2.0, c = 3.0 } : {a : Float, b : Float, c : Float} というレコード型の値があり、 > recodeB = { recodeA| b = 12 } { a = 1.0, b = 12.0, c = 3.0 } : {a : Float, b : Float, c : Float} とすることで、実現できる。 元にするレコード 更新したいフィールドと、その値 (複数ある場合、カンマで区切る) これの”b”フィールドが’12’になった値が欲しい場合、
  12. ・updateの記述 -- update update : Msg -> Model -> Model

    update msg model = case msg of SetLeftTerm f -> { model | left_term = f } SetRightTerm f -> { model | right_term = f } Addition -> { model | answer = Maybe.map2 (+) model.left_term model.left_term } Subtraction -> { model | answer = Maybe.map2 (-) model.left_term model.left_term } “update”は、状態の更新を行う関数です。 大体の場合、複数種類の更新が考えられるので、更新指示(Msg)を受け取り、それにより、状態を更新します。 なので、更新指示と現在の状態を受け取り、更新後の状態を返す関数として定義します。 Msgのバリアント毎にcase分岐する 更新後の状態として、 新しいModel型の値を返す パターンマッチ時に、 添えられたFloat値を、’f’に拘束している。
  13. ・Htmlモジュール Htmlモジュールは、 主に、HTML上での基本的なタグ等を関数として、提供します。 ほぼすべての関数が Html msg 型を返す様になっており、 また、 多くの関数が、 List

    (Html msg) 型を受け取る様になっているため、入れ子構造にできるようになっています。 div [] [ text "情報を入力してください" , div [] [ text "名前 : " , input [] [] ] , div [] [ text "年齢 : " , input [] [] ] , button [] [ text "登録" ] ] よく使われる関数は、 ・div ・text ・input ・button 等で、右のように使用されます。 こんな表示になる div : List (Attribute msg) -> List (Html msg) -> Html msg 例えば、divは、 と定義され、1つ目の引数が、属性のリストで、2つ目の引数が、子要素のリストとなります。
  14. ・Html.Attributesモジュール Html.Attributesモジュールは、 主に、HTML上での基本的な属性等を関数として、提供します。 ほぼすべての関数が Attribute msg 型を返す様になっています。 div [ style

    "border" "solid 2px blue" , style "width" "200px" ] [ text "情報を入力してください" , div [] [ text "名前 : " , input [] [] ] , div [] [ text "年齢 : " , input [ style "width" "20px” ] [] ] , button [] [ text "登録" ] ] こんな表示になる style : String -> String -> Attribute msg 例えば、よく使われるstyleは、 と定義され、1つ目の引数が、属性名で、2つ目の引数が、その値の文字列となります。
  15. ・Html.Eventsモジュール Html.Eventsモジュールは、 主に、HTML上での基本的な属性等を関数として、提供します。 こちらも、ほぼすべての関数が Attribute msg 型を返す様になっています。 div [] [

    text "情報を入力してください" , div [] [ text "名前 : " , input [ onInput SetName ] [] ] , div [] [ text "年齢 : " , input [ onInput SetAge ] [] ] , button [ onClick SignUp ] [ text "登録" ] ] onClick : msg -> Attribute msg 例えば、よく使われるonClickは、 と定義され、1つ目の引数が、属性名で、2つ目の引数が、その値の文字列となります。 よく使われる関数は、 ・onClick ・onInput 等で、右のように使用されます。
  16. ・viewの記述(全体像) view関数の全体像は、左のものです。 -- view view : Msg -> Model ->

    Model view msg model = div [] [ div [ style "float" "left" ] [ text "第1項" , div [] [] , input [ style "width" "55px" , onInput (¥strf -> SetLeftTerm <| String.toFloat strf) ] [] ] , div [ style "float" "left" ] [ br [] [] , div [ style "margin-left" "10px" , style "margin-right" "10px" ] [ text "?" ] ] , div [] [ text "第2項" , div [] [] , input [ style "width" "55px" , onInput (SetRightTerm << String.toFloat) ] [] ] , br [] [] , div [] [ text "? : " , button [ onClick Addition , style "margin-right" "10px” ] [ text " + " ] , button [ onClick Subtraction ] [ text " - " ] ] , br [] [] , div [] (case model.answer of Just ans -> [ text <| "計算結果 : " ++ String.fromFloat ans ] Nothing -> [] ) ] 先程の画面図に当てはめると... ① ② ③ ④ ③ ① ② ⑤ ⑥ ⑦ ① ② ③ ① ② ③ ④ ⑤ ⑥ ⑦
  17. [ div [ style "float" "left" ] [ text "第1項"

    , div [] [] , input [ style "width" "55px" , onInput (¥strf -> SetLeftTerm <| String.toFloat strf) ] [] ] , div [ style "float" "left" ] [ br [] [] , div [ style "margin-left" "10px" , style "margin-right" "10px" ] [ text "?" ] ] , div [] [ text "第2項" , div [] [] , input [ style "width" "55px" , onInput (SetRightTerm << String.toFloat) ] [] ] ・viewの記述(①) まず①部分 ① ③ ① ② divを横に並べるためにstyleを設定 入力時のイベントに、Msg型の値を設定 onInputは、 onInput : (String -> msg) -> Attribute msg と定義されているが、 今回設定したいSetLeftTermは、Maybe Float型を受け取る。 そのため、 String型を受け取りMaybe Float型に変更する関数を繋ぎ、 全体で、String型を受け取りMsg型を返す関数を、 ラムダ関数として定義して使う。 (<<) : (b -> c) -> (a -> b) -> a -> c 上記は、このようにも書ける これは、 を使って、関数合成している。
  18. , div [] [ text "? : " , button

    [ onClick Addition , style "margin-right" "10px” ] [ text " + " ] , button [ onClick Subtraction ] [ text " - " ] ] ・viewの記述(②,③) 次に②部分 クリック時のイベントに、Msg型の値を設定 ボタン同士の隙間を上げるために、styleを設定 ② ④ ⑤ ⑥ ボタンに表示される文字設定 , div [] (case model.answer of Just ans -> [ text <| "計算結果 : " ++ String.fromFloat ans ] Nothing -> [] ) 最後に③部分 ③ ⑦ 状態(modelのanswer)によって、内容を変える answerが、値を持っていれば、結果表示 answerが、値を持っていなけれ、何もださない
  19. ・動作の確認 $ elm reactor 作成したWebアプリケーションの動作の確認は、elm reactorを利用すると、簡単に行えます。 コンソールを開いて、作業ディレクトリに移り、以下のコマンドを実行します。 その後、webブラウザで http://localhost:8000 にアクセスしましょう。

    右の様に、rectorを実行した位置のディレクトリ構造が表示されます。 そこから srcを選択し、次の画面で、main.elmを選択します。 Elmファイルを選択すると、自動でビルドが走り、エラーがある場合、エラーを表示します。 エラーが無ければ、view関数で定義されたHTMLが表示されます。 このreactorを利用することで、開発中のwebページの確認や、 テストをして、その結果を見ながらの作成も可能です。
  20. ・Elmファイルのコンパイル Elmファイルに書いたプログラムを実際に、使用する際は、コンパイルが必要になります。 $ elm make src/Main.elm --output=elm.js コンソールを開いて、作業ディレクトリに移り、以下のコマンドを実行します。 コンパイルは、elm makeを使用し、JavaScriptファイルとして出力することが多いです。

    Success! Compiled 1 module. 以下のような出力が出れば、成功です。 これにより、作業ディレクトリに、 ・elm-stuff/ (各オブジェクトファイルが入っている) ・elm.js ができます。 この elm.js を、HTMLファイルからコールすれば、webアプリケーションとなる。
  21. ・HTMLファイルからのコール Elmファイルをコンパイルして、作成したelm.jsを、HTMLファイルからコールすることで、webアプリケーションになる。 <!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Calculator</title> <script

    src="elm.js"></script> </head> <body> <div id="elm"></div> <script> var app = Elm.Main.init({ node: document.getElementById('elm') }); </script> </body> </html> index.htmlを作成し、中身に、以下を記述します。 ページタイトルをここに記入 コンパイルして生成したjsファイルをここで指定 Elmの呼び出し このindex.htmlをブラウザで開くと、作成したWebアプリケーションが表示されます。