LoginSignup
13
8

More than 5 years have passed since last update.

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

Posted at

この記事は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

ソースコード

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

13
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
8