の続き
ellie-app.com
やること
- [x]
List
でTODOリストを保持する - [x]
List
をHTML
に吐き出す - [x]
input
に入力した値をbutton
を押すことでリストに追加 - [x] TODO横に削除ボタンを作成、押したら消せるように
- [ ] TODOをダブルクリックで
input
化して更新できるように - [ ]
localstorage
に保存できるようにする - [ ] 見た目オシャンティーにする
やったこと
空のTODOは追加できないようにする
ERROR型の定義
type ErrorMessage = NO_ERROR | TODO_IS_EMPTY
NO_ERROR
エラーなしTODO_IS_EMPTY
追加しようとしているTODOが空のエラー
エラーのパターンを制御して出し分けたくて型作ってVariant
を定義した
Model
へ追加
type alias Model = { todoList : List Todo , inputText : String , errorMessage : ErrorMessage }
定義したErrorMessage
型でエラーを定義
後々view
で表示するために使う
init
initialModel : Model initialModel = { todoList = [] , inputText = "" , errorMessage = NO_ERROR }
初期状態ではエラーは無いので、エラーなしを示す
NO_ERROR
を設定
update
関数のAdd
処理
update : Msg -> Model -> Model update msg model = case msg of Add -> case String.isEmpty model.inputText of True -> { model | errorMessage = TODO_IS_EMPTY } False -> { model | todoList = { index = getNextTodoIndex model.todoList , todo = model.inputText } :: model.todoList , inputText = "" , errorMessage = NO_ERROR }
String.isEmpty
を使って追加対象のTODOが空文字かどうかチェックTrue
(空文字の場合)
errorMessage
に空を示すTODO_IS_EMPTY``Variant
を設定False
(空文字ではない場合) 元々のTODO処理にerrorMessage = NO_ERROR
を足して
エラーメッセージが無い状態にするようにした
showErrorMessage
関数
showErrorMessage : ErrorMessage -> Html Msg showErrorMessage errorType = case errorType of NO_ERROR -> div [] [] TODO_IS_EMPTY -> div [] [ text "空文字のTODOは作成できません" ]
ErrorMessage
を引数に受けて、エラーのHTMLを返す関数
NO_ERROR
なら空のdiv
TODO_IS_EMPTY
なら空文字エラーのメッセージを持ったdiv
を返すように実装した
これでエラーパターンを増やしたり減らした時に検知できるはず
view
view : Model -> Html Msg view model = div [] [ h1 [] [ text "TODO LIST" ] , showErrorMessage model.errorMessage
showErrorMessage
にmodel.errorMessage
を渡して呼ぶ
エラーの追加はupdate
関数のAdd
表示はshowErrorMessage
という形で分けた
指定したTODOを削除できるようにする
Todo
型エイリアスを作ってindex
を足した
type alias Todo = { id : Int , todo : String }
TODOの削除にあたってid
が必要になったので
todo
とid
はセットのレコードだなと考えてTodo
型エイリアスを作った
name
,bio
をUser
型エイリアスにするやり方を参考にした
型エイリアス · An Introduction to Elm
削除処理を実装
filterByIndex : List Todo -> Int -> List Todo filterByIndex todoList id = List.filter (\todo -> todo.id /= id) todoList
todoList
とid
を引数に受けてtodoList
内から渡したid
と一致するものを
削除したtodoList
を返すようにした
ここもっといい作りある気がしてモヤモヤしている
Msg
とupdate
に削除処理を追加
type Msg = Add | UpdateTextField String | Delete Int update Delete id -> { model | todoList = filterByIndex model.todoList id }
Model
はid
を指定して削除する想定なのでInt
を受けるようにした
update
は先程作った削除処理を呼び出しているだけ
ここでid
を振る処理がないな、と気づく
TODOに一意なid
を振る処理を実装
getNextTodoId : List Todo -> Int getNextTodoId todoList = case List.head todoList of Just todo -> todo.id + 1 Nothing -> 0
TODOリストを渡すと次のid
を返してくれるだけの処理
(削除すると連番ではなくなるけど、一意であればOKなので問題ない)
単なるcounter
を用意しても良かったけどMaybe
型を使ってみたかった
TODO追加時にid
を振るように
{ model | todoList = Todo (getNextTodoId model.todoList) model.inputText :: model.todoList , inputText = "" , errorMessage = NO_ERROR }
ここもガイドを参考にした
https://guide.elm-lang.jp/types/type_aliases.html#レコードコンストラクタ
型エイリアスを作るとレコードコンストラクタが生まれるので、楽にレコードが作れた
Todo
の第一引数はid
なので、一意なid
を振るgetNextTodoId
を呼ぶ
→それぞれTodo
の引数扱いになって怒られたので()
で囲んだ
第二引数はtodo
なので、入力されたTODOを入れた
::
でのList
追加は変わらず
ここで`TODO表示処理使いづらいな…と思い改造する
元のTODO一覧表示を改造した
showTodoList : List String -> Html msg showTodoList todoList = todoList |> List.map (\todo -> li [] [ text todo ]) |> ul [] view , showTodoList model.todoList
元々ul li
の形をまとめて作るようにしていたけど
必要だったのは「1つのTodo
をHTML
にする」で
ul li
の形までするのはやりすぎだった
List化やul[]
内の要素にするかは他の責務にすることにした
showTodo : Todo -> Html Msg showTodo todo = li [] [ text todo.todo ]
急にシンプルでわかりやすい関数になった
これでul
ではなくdiv
内の要素にしたり
li
ではなくdiv
にしたりと変更しやすくなった
, ul [] (List.map showTodo model.todoList)
呼び出す側でul
で囲んでList.map
でList化するようにした
TODO表示処理に削除ボタン追加
showTodo : Todo -> Html Msg showTodo todo = li [] [ text (todo.todo ++ " ") , button [ onClick (Delete todo.id) ] [ text "delete" ] ]
ちょっとダサかったので(todo.todo ++ " ")
ボタンとのスペース追加
削除処理はid
が必要なのでtodo.id
を渡すようにした
ここにボタン足すだけで全部のTODOにボタンが追加されるので楽だった
いい修正をした気がする
これで削除ができるようになった やったね
まとめ
- リスト操作が学べた
- 削除
List.map
の基礎的な使い方
counter
サンプルのInt
を絞るやり方が活きた- 型大事だなってなった、ありがとうございます
- とりあえず関数化するようになった
カリー化?高階関数?が今後の課題
ナマステはあ⁇、。インドじんは。‼︎うし。、⁈!
— मत्स्य (@Matsya_) 2020年4月11日 - 型エイリアスの便利さに気づいた
- レコードコンストラクタが便利
- レコードを型として扱えるのが(・∀・)イイ!!
java
とかでも型作れば出来るけど、サクッと書けて気持ちいい
今後の事やおまけ
- カリー化、高階関数わかるともっときれいに書けそう
filterByIndex
を作ったけど、微妙に使いづらい関数な気が - 削除方法他にもあった気がする
id指定じゃなくてhead
とtail
組み合わせても行けると思う
id指定だと全部捜査するから遅い気がする index
使う処理に慣れすぎてまだまだList
操作に慣れない
忘備録的な学習メモ的なブログになっちゃってるけど
後々はもっと誰かの助けになるようなブログにしたい
100日後にはもっと成長できてるといいな…