Posted at

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


この記事はElm+Firebaseでチャットアプリを作るのエントリーです。



この記事で行うこと

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

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を言語内で関数を用いて表現します。

Htmlを表現する関数は、

div : List (Attribute msg) -> List (Html msg) -> Html msg

のように表現され、

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

と読むことができます。div関数の第1引数は、Htmlの属性のリストを表現しています。第2引数は、Htmlの小要素のリストを表現しています。小要素に文字列を入れるときは、text関数で表現します。

-- <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%;
}
}
}



実装後の画面

スクリーンショット 2018-12-25 0.08.25.png


ソースコード

この時点でのソースコードです。