この記事は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%;
}
}
}
実装後の画面
ソースコード
この時点でのソースコードです。