Elmでビュー(チャット画面)を作る

> この記事は[Elm+Firebaseでチャットアプリを作る](https://qiita.com/ababup1192/items/5f64a5f6f00664ee7981)のエントリーです。

# この記事で行うこと

この記事では、Elmでビュー(チャット用の画面)を作成します。

[elm-webpack-starter](https://github.com/simonh1000/elm-webpack-starter)をあらかじめクローンしてください。主に*src/*配下のファイルを変更します。

## 不要コードのお掃除

さっそくお掃除でごめんなさい。元々のコードがなかなか良いサンプルなのですが、今回の学習には不要な部分が多いのでお掃除します。

```index.js
'use strict';

require("./styles.scss");

const { Elm } = require('./Main');
const app = Elm.Main.init({});
```

今回メインになるのは、view関数なので、それ以外のコードを一旦お見せします。

```Main(view以外).elm
module Main exposing (Model, Msg(..), init, main, update, view)

import Browser
import Html exposing (..)
import Html.Attributes exposing (class, href, placeholder, type_)



-- ---------------------------
-- MODEL
-- ---------------------------


type alias Model =
    {}


init : () -> ( Model, Cmd Msg )
init _ =
    ( {}, Cmd.none )



-- ---------------------------
-- UPDATE
-- ---------------------------


type Msg
    = NoOp


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    ( model, Cmd.none )

-- ---------------------------
-- MAIN
-- ---------------------------


main : Program () Model Msg
main =
    Browser.document
        { init = init
        , update = update
        , view =
            \m ->
                { title = "Elm Firebase Chat"
                , body = [ view m ]
                }
        , subscriptions = \_ -> Sub.none
        }
```

Elmでは[Html](https://package.elm-lang.org/packages/elm/html/latest/Html)を言語内で関数を用いて表現します。
Htmlを表現する関数は、

*[div](https://package.elm-lang.org/packages/elm/html/latest/Html#div) : List (Attribute msg) -> List (Html msg) -> Html msg*

のように表現され、

*関数名: 第1引数 -> 第2引数 -> ... -> 戻り値*

と読むことができます。div関数の第1引数は、[Htmlの属性](https://package.elm-lang.org/packages/elm/html/latest/Html-Attributes)のリストを表現しています。第2引数は、Htmlの小要素のリストを表現しています。小要素に文字列を入れるときは、[text](https://package.elm-lang.org/packages/elm/html/latest/Html#text)関数で表現します。

```elm
-- <div class="foo"><h1>bar</h1></div>
view model =
    div [ class "foo" ] [
        h1 [] [ text "bar" ] 
    ]
```

法則さえ分かれば後は機械的に思い描いたHtmlを変換していくだけです。view関数の実装のみ抜粋します。

```Main(viewだけ).elm
-- ---------------------------
-- VIEW
-- ---------------------------


view : Model -> Html Msg
view model =
    div [ class "page" ]
        [ section [ class "card" ]
            [ div [ class "card-header" ]
                [ text "Elm Chat"
                ]
            , div [ class "card-body" ]
                [ div [ class "media" ]
                    [ div [ class "media-left" ]
                        [ a [ href "#", class "icon-rounded" ] [ text "S" ]
                        ]
                    , div [ class "media-body" ]
                        [ h4 [ class "media-heading" ] [ text "Suzuki Taro Date:2016/09/01" ]
                        , div [] [ text "この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。" ]
                        ]
                    ]
                , hr [] []
                , div
                    [ class "media" ]
                    [ div [ class "media-body" ]
                        [ h4 [ class "media-heading" ] [ text "Tanaka Jiro Date:2016/09/01" ]
                        , div [] [ text "この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。" ]
                        ]
                    , div
                        [ class "media-right" ]
                        [ a [ href "#", class "icon-rounded" ] [ text "T" ]
                        ]
                    ]
                ]
            ]
        , section []
            [ form [ class "chart-form pure-form" ]
                [ div [ class "input-group" ]
                    [ input [ type_ "text", class "", placeholder "Comment" ] []
                    , button [ class "pure-button button-secondary" ] [ text "SNED" ]
                    ]
                ]
            ]
        ]
```

```styles.scss
@import '~purecss/build/pure.css';

html, body {
  height: 100%;
  font-family: "Hiragino Kaku Gothic ProN","メイリオ", sans-serif;
  font-size: 14px;
  line-height: 1.42857143;
  background-color: rgba(255, 251, 251, 0.78);
}

.button-secondary {
  color: white;
  border-radius: 4px;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);

  background: rgb(66, 184, 221);
}

.page {
  max-width: 960px;
  height: 100%;
  margin: 0 auto;
  padding: 10px;
  background-color: #fff;
  border-left: 1px solid #DDDDDD;
  border-right: 1px solid #DDDDDD;
  box-shadow: 0 0 15px #DDDDDD;

  .card {
    border: 1px solid #e5e5e5;
    border-radius: 4px;

    .card-header {
      padding: 10px;
      background-color: #f5f5f5;
      border-bottom: 1px solid #e5e5e5;
    }

    hr {
      margin: 0 20px;
      border: 1px solid #f5f5f5;
    }

    .media-body {
      width: 100%;
      padding: 18px;
      margin: 0 10px;
      background-color: #F5F5F5;
      border-radius: 10px;
      font-size: 15px;
      word-break: break-all;

      h4 {
        margin: 4px;

        .media-heading {
          color: #999999;
          font-size: 13px;
        }
      }
    }

    .card-block {
      padding: 10px;
    }

    .card-body {
      .media {
        display: flex;
        padding: 20px;
      }
    }

    a.icon-rounded {
      display: block;
      font-size: 26px;
      color: #fff;
      text-align: center;
      margin: 5px;
      padding: 5px;
      width: 50px;
      height: 50px;
      line-height: 50px;
      border-radius: 50%;
      text-decoration: none;
    }

    .media-left {
      a.icon-rounded {
        background-color: #f0ad4e;
      }
    }


    .media-right {
      a.icon-rounded {
        background-color: #5cb85c;
      }
    }
  }

  .chart-form {
    margin: 20px 0;

    .input-group {
      display: flex;
    }

    input {
      width: 100%;
    }
  }
}
```

## 実装後の画面

<img width="1020" alt="スクリーンショット 2018-12-25 0.08.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/48687/aac426f0-d8ec-ece5-04bc-953ac9732aef.png">

## ソースコード

この時点での[ソースコード](https://github.com/ababup1192/elm-firebase-chat/tree/718d48fd122515b5a5242294858f25344c48460c)です。