これまでの復習
Elm 0.18で作るTodoアプリ(1)「固定値のTodoをTodoリストへ追加できるようにする」
Elm 0.18で作るTodoアプリ(2)「テキストボックスへ入力したTodoをTodoリストへ追加できるようにする」
前回をベースにTodoアプリを拡張していきます。
今回のテーマ
Todoのチェック用にチェックボックスを設置する
ファイル構成
- Main.elm:大元のプログラム。基本的には配下のモジュールへ委譲するのみ。
- TodoList.elm:Todoのリスト管理を行う
- TodoCreator.elm:(今回は変更なし)テキストボックスを管理する。
- Todo.elm:(New!)一つのTodoを管理する。
これまでは、一つのTodoが単なる文字列でしたが、チェックボックスの追加などの一つのTodoに対する機能追加が考えられます。
そこで、管理しやすくするためにTodoListからTodoを切り出すことにしました。
実装
Todo.elmの新規作成
- moduleの宣言とimport関連
module Todo exposing (..)
import Html exposing (..)
import Html.Attributes exposing (style, type_, checked)
import Html.Events exposing (onClick)
- モデルの定義
基本的にはTodoListからの切り出しですが、外部から任意のTodoを作成できるようにするためにinitialModelの代わりにnew関数を新規作成しました。
チェックボックスのハンドリング用にToggleDoneというメッセージを定義しています。
-- model
type alias Model =
{ done : Bool
, item : String
}
new : Bool -> String -> Model
new b s =
{ done = b
, item = s
}
type Msg
= NoOp
| ToggleDone String
- updateの定義
-- update
update : Msg -> Model -> Model
update message model =
case message of
NoOp ->
model
ToggleDone s ->
if s == model.item then
{ model | done = not model.done }
else
model
ToggleDoneの引数でTodo相当の文字列をもらい、該当のTodoのみ状態を変更させるようにしています。
- viewの定義
-- view
view : Model -> Html Msg
view model =
li []
[
checkbox (ToggleDone model.item) model
]
checkbox : msg -> Model -> Html msg
checkbox msg model =
label []
[ input [ type_ "checkbox", checked model.done, onClick msg ] []
, viewItem model
]
viewItem : Model -> Html msg
viewItem model =
if model.done == False then
text model.item
else
s []
[ span [ style [ ("color", "gray") ] ]
[ text model.item ]
]
チェックボックスのハンドリングをするcheckbox関数を定義しています。
checked属性にmodel.doneを設定し、チェック状態が変わるようにしました。
viewItemは前回のTodoListのviewItemから変更し、doneの状態によって見栄えを変えるようにしました。
TodoList.elmの変更
- ファイル全体
module TodoList exposing (..)
import Html exposing (..)
import Html.Attributes exposing (class)
import Html.Events exposing (onClick)
+import Todo
-- model
-type alias ToDo =
- { done : Bool
- , item : String
- }
+type alias TodoModel = Todo.Model
type alias Model =
- { todoList : List ToDo
+ { todoList : List TodoModel
}
initialModel : Model
initialModel =
{ todoList =
- [ ToDo False "item1"
- , ToDo False "item2"
- , ToDo False "item3"
+ [ Todo.new False "task1"
+ , Todo.new False "task2"
+ , Todo.new True "task3"
+ , Todo.new False "task4"
]
}
type Msg
= NoOp
| AddNew
+ | TodoMsg Todo.Msg
-- update
-update : Msg -> ToDo -> Model -> ( Model, Cmd Msg )
+update : Msg -> TodoModel -> Model -> ( Model, Cmd Msg )
update message todo model =
case message of
NoOp ->
model ! []
AddNew ->
{ model | todoList = model.todoList ++ [todo] } ! []
+ TodoMsg subMsg ->
+ let
+ updatedTodoList =
+ List.map (Todo.update subMsg) model.todoList
+ in
+ { model | todoList = updatedTodoList } ! []
+
-- view
view : Model -> Html Msg
view model =
div []
[
div [ class "p2" ]
[ addButton
- , viewItems model.todoList
+ , viewList model
]
]
addButton : Html Msg
addButton =
div []
[ button [ onClick AddNew ] [ text "Add" ] ]
-viewItems : List ToDo -> Html Msg
-viewItems models =
- ul [] (List.map viewItem models)
-
-viewItem : ToDo -> Html Msg
-viewItem model =
- li [] [
- text model.item
- ]
+viewList : Model -> Html Msg
+viewList model =
+ (ul []
+ (List.map Todo.view model.todoList))
+ |> Html.map TodoMsg
initialModelの初期値変更以外は、基本的にTodoの切り出しによる修正です。
Todoで処理すべきものはTodo.elmへ委譲しています。
Main.elmの変更
- ファイル全体
module Main exposing (..)
import Html exposing (Html, program)
import TodoCreator
import TodoList
+import Todo
main : Program Never Model Msg
main =
program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
-- model
type alias Model =
{ todoCreator : TodoCreator.Model
, todoList : TodoList.Model
}
initialModel : Model
initialModel =
{ todoCreator = TodoCreator.initialModel
, todoList = TodoList.initialModel
}
init : ( Model, Cmd Msg)
init =
( initialModel, Cmd.none)
type Msg
= TodoCreatorMsg TodoCreator.Msg
| TodoListMsg TodoList.Msg
-- update
update : Msg -> Model -> ( Model, Cmd Msg )
update message model =
case message of
TodoCreatorMsg subMsg ->
let
( updatedCreator, todoCreatorCmd ) =
TodoCreator.update subMsg model.todoCreator
in
( { model | todoCreator = updatedCreator }, Cmd.map TodoCreatorMsg todoCreatorCmd )
TodoListMsg subMsg ->
let
( updatedTodoListModel, todoListCmd ) =
- TodoList.update subMsg (TodoList.ToDo False model.todoCreator.inputStr) model.todoList
+ TodoList.update subMsg (Todo.new False model.todoCreator.inputStr) model.todoList
in
( { model | todoList = updatedTodoListModel }, Cmd.map TodoListMsg todoListCmd )
こちらも基本的にTodoの切り出しによる修正です。
実行結果
前回と同様にmakeして、作成したindex.htmlをブラウザで見ます。
$ elm-make Main.elm --output index.html --debug
チェックボックスが追加され、チェックすると文字のデザインが変化することが分かると思います。
おわりに
Todoを要素として切り出し、それに伴った変更とチェックボックスの追加を行ってきました。
次回はTodoList側の機能拡張(Todo完了数表示など)を行っていきたいと思います。
次回:Elm 0.18で作るTodoアプリ(4)