フォーム
ここでは基本的なフォームを作成します。名前、パスワード、パスワード(確認用)のフィールドを持ったフォームです。また「2つのパスワードが一致しているか」という簡単な入力の検証も行います。
プログラムの全体像をここに載せます。青い "Edit" ボタンをクリックして、オンラインエディタでこのコードをお好きなように触ってみてください。レコードのフィールド名にある password
や placholder
関数の名前などをわざと打ち間違えて、コンパイラーのエラーメッセージを見てみましょう。今すぐ青いボタンをクリック!
import Browser
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput)
-- MAIN
main =
Browser.sandbox { init = init, update = update, view = view }
-- MODEL
type alias Model =
{ name : String
, password : String
, passwordAgain : String
}
init : Model
init =
Model "" "" ""
-- UPDATE
type Msg
= Name String
| Password String
| PasswordAgain String
update : Msg -> Model -> Model
update msg model =
case msg of
Name name ->
{ model | name = name }
Password password ->
{ model | password = password }
PasswordAgain password ->
{ model | passwordAgain = password }
-- VIEW
view : Model -> Html Msg
view model =
div []
[ viewInput "text" "Name" model.name Name
, viewInput "password" "Password" model.password Password
, viewInput "password" "Re-enter Password" model.passwordAgain PasswordAgain
, viewValidation model
]
viewInput : String -> String -> String -> (String -> msg) -> Html msg
viewInput t p v toMsg =
input [ type_ t, placeholder p, value v, onInput toMsg ] []
viewValidation : Model -> Html msg
viewValidation model =
if model.password == model.passwordAgain then
div [ style "color" "green" ] [ text "OK" ]
else
div [ style "color" "red" ] [ text "Passwords do not match!" ]
フィールドが複数ある点を除けば、テキストフィールドで紹介したコードによく似ています。
Model
いつものように、モデルを考えることからはじめます。3つのテキストフィールドが作られることが分かっているので、このようにします。
type alias Model =
{ name : String
, password : String
, passwordAgain : String
}
最初は最低限のモデルから始めましょう。用意するフィールドは1つだけでも構いません。そのあとで view
と update
関数に取りかかります。すると大概、モデルを拡張しなければならないことが明らかになります。こんなふうにモデルを少しずつ組み立ててることで、開発プロセスの間ずっと、ちゃんと動作するプログラムを相手にすることができます。作成中のプログラムはまだすべての機能を備えてはいませんが、少しずつそこに近づけていくのです。
Update
これは update
がおおまかにどんな形になるか、よいアイデアが浮かびやすい例ですね。間違いなく「1つの名前、2つのパスワード」からなる3つのフィールドを変更できるようにしておきたいので、それぞれのフィールドに対応するメッセージが必要です。
type Msg
= Name String
| Password String
| PasswordAgain String
メッセージが3種類あるということは update
にも対応する3つの分岐が必要そうです。
update : Msg -> Model -> Model
update msg model =
case msg of
Name name ->
{ model | name = name }
Password password ->
{ model | password = password }
PasswordAgain password ->
{ model | passwordAgain = password }
パターンマッチの3つの分岐のそれぞれで、レコード更新構文を使って適切なフィールドだけを変更しています。分岐が増えたこと以外は前の節に出てきた例とよく似ていますね。
ですが、 view
ではすこし気の利いたことをしています。
View
view
関数では、まとまりのあるコードにするために補助関数を使っています。
view : Model -> Html Msg
view model =
div []
[ viewInput "text" "Name" model.name Name
, viewInput "password" "Password" model.password Password
, viewInput "password" "Re-enter Password" model.passwordAgain PasswordAgain
, viewValidation model
]
以前の例では直接 input
や div
を使っていました。どうしてここではそうしないのでしょうか?
ElmでHTMLを書くときに素敵なのは、input
や div
などが全てごく普通の関数だということです。これらの関数は引数として (1) 属性のリストと、(2) 子ノードのリストを受け取ります。普通の関数を使っているおかげで、ビューを組み立てるときにもElmの力を余すことなく使えるのです! まさにここでやっているように、繰り返し使うコードは補助関数に切り出してリファクタリングすることができます。
view
関数では3回 viewInput
を呼び出しています。
viewInput : String -> String -> String -> (String -> msg) -> Html msg
viewInput t p v toMsg =
input [ type_ t, placeholder p, value v, onInput toMsg ] []
Elmで viewInput "text" "Name" "Bill" Name
と書いたとき、画面に表示されるHTMLは <input type="text" placeholder="Name" value="Bill">
のようになります。
4つ目のノードはもっと面白いです。 viewValidation
の呼び出しです。
viewValidation : Model -> Html msg
viewValidation model =
if model.password == model.passwordAgain then
div [ style "color" "green" ] [ text "OK" ]
else
-- 訳注: "Passwords do not match!" は "パスワードが一致しません!" の意味です。
div [ style "color" "red" ] [ text "Passwords do not match!" ]
この関数はまず、二つのパスワードを比較します。一致すると、緑色の文字で入力に問題がないことを示すメッセージを表示します。一方、一致しない場合は赤色の文字でエラーを修正するために役立つメッセージを表示します。
こうした補助関数をみると、HTMLのシンタックスをそのまま用いるのではなく、現状のように通常のElmコードでHTMLを表現できるHTMLライブラリの利点がわかります。すべてのコードをベタ書きでviewに書くことももちろんできますが、Elmにおいて補助関数をつくって部品をくくりだすことはいたって普通のことですので、view関数のコードだろうが補助関数を使えば良いんです。「コードがわかりづらくなってきたら、補助関数に分けてみよう!」と考えてみましょう。
練習問題: ここからオンラインエディタを開いてこの例のコードを表示し、補助関数の
viewValidation
に次の機能を追加してみてください。
- パスワードが8文字より長いか確認する。
- パスワードが大文字、小文字、数字を含むか確認する。
この問題を解くときは
String
モジュールに用意された関数を使いましょう。
注意: HTTPリクエストを扱う前にまだ学ぶことがあります。試すのはHTTPのパートまで読んでからにしましょう。適切に順を追って説明していくので、かなり簡単になるはずです。
Note: 入力の検証を行うための汎用的なライブラリを作っても、苦労の割には大した結果を得られないでしょう。そんなライブラリを作らなくても、こうしたチェックはElmの通常の関数を使うだけで実現できる場合がほとんどだと思います。いくつかの引数をとって、
Bool
かMaybe
を返せばいいのです。例えば、2つの文字列が等しいかどうかをチェックするのに、あえてライブラリを使う必要なんてあるんでしょうか? 我々が知る限りでは、特別に何か追加でライブラリを使わなくても、実現したい特定の状況に合わせた具体的なロジックを書くことが最もシンプルなコードにつながります。なので、より複雑な方法が必要だと判断する前に、必ずまずこの最も単純なやり方を試してみてください!