Elm 0.18で作るTodoアプリ(3)

これまでの復習

[Elm 0.18で作るTodoアプリ(1)](https://qiita.com/tomluck/items/872787cda6682834a3a1)「固定値のTodoをTodoリストへ追加できるようにする」
[Elm 0.18で作るTodoアプリ(2)](https://qiita.com/tomluck/items/ec95df77fb54c9d54275)「テキストボックスへ入力したTodoをTodoリストへ追加できるようにする」

前回をベースにTodoアプリを拡張していきます。

# 今回のテーマ
Todoのチェック用にチェックボックスを設置する

# ファイル構成
- Main.elm：大元のプログラム。基本的には配下のモジュールへ委譲するのみ。
- TodoList.elm：Todoのリスト管理を行う
- TodoCreator.elm：(今回は変更なし)テキストボックスを管理する。
- Todo.elm：<font color="Red">(New!)</font>一つのTodoを管理する。

これまでは、一つのTodoが単なる文字列でしたが、チェックボックスの追加などの一つのTodoに対する機能追加が考えられます。
そこで、管理しやすくするためにTodoListからTodoを切り出すことにしました。

# 実装

## Todo.elmの新規作成

- moduleの宣言とimport関連

```hs
module Todo exposing (..)

import Html exposing (..)
import Html.Attributes exposing (style, type_, checked)
import Html.Events exposing (onClick)

```

- モデルの定義

基本的にはTodoListからの切り出しですが、外部から任意のTodoを作成できるようにするためにinitialModelの代わりにnew関数を新規作成しました。
チェックボックスのハンドリング用にToggleDoneというメッセージを定義しています。

```hs
-- 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の定義

```hs
-- 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の定義

```hs
-- 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の変更

- ファイル全体

```diff
 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の変更

- ファイル全体
 
```diff
 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
```

チェックボックスが追加され、チェックすると文字のデザインが変化することが分かると思います。
![FF_elm_study3.png](https://qiita-image-store.s3.amazonaws.com/0/129853/f1943138-4e02-8d33-d0ad-2f63ee87e602.png)

# おわりに
Todoを要素として切り出し、それに伴った変更とチェックボックスの追加を行ってきました。

次回はTodoList側の機能拡張(Todo完了数表示など)を行っていきたいと思います。
次回：[Elm 0.18で作るTodoアプリ(4)](https://qiita.com/tomluck/items/5ba609c72cd35e7959c8)