Elm 0.18로 만드는 Todo 앱 (5)

10795 단어 Elm
지금까지의 복습

Elm 0.18로 만드는 Todo 앱(1) 「고정치의Todo를Todo리스트에 추가할 수 있게 한다」
Elm 0.18로 만드는 Todo 앱 (2) “텍스트 박스에 입력한 Todo를 Todo 목록에 추가할 수 있도록 한다”
Elm 0.18로 만드는 Todo 앱 (3) “Todo 확인을 위해 체크박스 설치하기”
Elm 0.18로 만드는 Todo 앱 (4) 「TodoList측의 기능 확장을 한다.」

마지막으로 토도 앱을 확장합니다.

이번 테마



(최종회) 일의로 update 하는Todo를 결정할 수 있도록 기능확장한다.

파일 구성


  • Main.elm : 대원의 프로그램. 기본적으로는 부하의 모듈에 위양할 뿐.
  • TodoList.elm : Todo 목록 관리하기
  • TodoCreator.elm : (이번에는 변경 없음) 텍스트 박스를 관리한다.
  • Todo.elm : 하나의 Todo를 관리합니다.

  • 구현



    Todo.elm 수정


  • 모델 수정
  •  type alias Model = 
    -    { done : Bool
    +    { itemID : Int
    +    , done : Bool
         , item : String
         , del  : Bool
         }
    
    -new : Bool -> String -> Bool -> Model
    -new do s de = 
    -    { done = do
    +new : Int -> Bool -> String -> Bool -> Model
    +new itemID do s de = 
    +    { itemID = itemID
    +    , done = do
         , item = s
         , del = de
         }
    
     type Msg
         = NoOp
    -    | ToggleDone String
    -    | OnDelete String
    +    | ToggleDone Int
    +    | OnDelete Int
    

    Todo를 고유하게 식별하기 위해 itemID 속성을 추가합니다.
    이에 따라 new 함수의 인수나 메시지도 수정합니다.
  • 업데이트 수정
  • update : Msg -> Model -> Model
    update message model =
        case message of
             NoOp ->
                 model
    
    -        ToggleDone s -> 
    -            if s == model.item then
    +        ToggleDone itemID -> 
    +            if itemID == model.itemID then
                     { model | done = not model.done }
                 else
                     model
    
    -        OnDelete s -> 
    -            if s == model.item then
    +        OnDelete itemID -> 
    +            if itemID == model.itemID then
                     { model | del = True }
                 else
                     model
    

    ToggleDone과 onDelete의 인수를 문자열에서 itemID로 변경하고, itemID 단위로 해당하는 Todo만 상태를 변경하게 하고 있습니다.
  • view 정의
  • view : Model -> Html Msg
    view model = 
        li [] 
            [ 
    -            checkbox (ToggleDone model.item) model
    +            checkbox (ToggleDone model.itemID) model
            ]
    
     checkbox : Msg -> Model -> Html Msg
     checkbox msg model = 
         label []
             [ input [ type_ "checkbox", checked model.done, onClick msg ] []
             , viewItem model
             , viewDeleteButton model
             ]
    
     viewItem : Model -> Html Msg
     viewItem model =
         if model.done == False then
             text model.item
        else
            s [] 
                 [ span [ style [ ("color", "gray") ] ]
                         [ text model.item ]
                 ]
    
     viewDeleteButton : Model -> Html Msg
     viewDeleteButton model =
         span []
    -        [ button [ onClick (OnDelete model.item) ] [ text "x" ]
    +        [ button [ onClick (OnDelete model.itemID) ] [ text "x" ]
             ]
    

    메시지의 정의 변경에 맞게 인수를 itemID로 변경하고 있습니다.

    TodoList.elm 변경


  • 전체 파일
  • 
     module TodoList exposing (..)
    
     import Html exposing (..)
     import Html.Attributes exposing (class)
     import Html.Events exposing (onClick)
     import Todo
    
     type alias Model = 
         { todoList : List TodoModel
    +    , nextID : Int
         }
    
     initialModel : Model
     initialModel = 
         { todoList = 
    -        [ Todo.new False "task1" False
    -        , Todo.new False "task2" False
    -        , Todo.new True "task3" False
    -        , Todo.new False "task4" False
    +        [ Todo.new 1 False "task1" False
    +        , Todo.new 2 False "task2" False
    +        , Todo.new 3 True  "task3" False
    +        , Todo.new 4 False "task4" False
             ]
    +    , nextID = 5
         }
    
     type Msg
         = NoOp
         | AddNew
         | DeleteFinished
         | TodoMsg Todo.Msg
    
     -- update
    -update : Msg -> TodoModel -> Model -> ( Model, Cmd Msg )
    -update message todo model =
    +update : Msg -> String -> Model -> ( Model, Cmd Msg )
    +update message item model =
         case message of
             NoOp ->
                 model ! []
    
             AddNew -> 
    -            { model | todoList = model.todoList ++ [todo] } ! []
    +            let
    +                todo = 
    +                    Todo.new model.nextID False item False
    +            in
    +                { model | 
    +                    todoList = model.todoList ++ [todo] 
    +                    , nextID = model.nextID + 1
    +                } ! []
    
             DeleteFinished -> 
                 let
                     itemIsNotFinished todoModel = not todoModel.done
                 in
                     { model | 
                         todoList =  List.filter itemIsNotFinished model.todoList } ! []
    
             TodoMsg subMsg ->
                 let
                     itemIsNotDeleted todoModel = 
                         not todoModel.del
    
                     updatedTodoList = 
                         List.map (Todo.update subMsg) model.todoList
                 in
                     { model | 
                         todoList = List.filter itemIsNotDeleted updatedTodoList } ! []
    
     -- view
     view : Model -> Html Msg
     view model = 
         div []
             [
             div [ class "p2" ]
                 [ addButton
                 , viewCounter model
                 , deleteFinishedButton
                 , viewList model
                 ]
             ]
    
     addButton : Html Msg
     addButton = 
         div [] 
             [ button [ onClick AddNew ] [ text "Add" ] ]
    
     deleteFinishedButton : Html Msg
     deleteFinishedButton = 
         div [] 
             [ button [ onClick DeleteFinished ] [ text "Delete Finished" ] ]
    
     viewCounter : Model -> Html Msg
     viewCounter model = 
         div []
             [ text ("Finished Task: " 
                 ++ toString ( countDoneItems model )
                 ++ "/" 
                 ++ toString ( List.length model.todoList ) ) 
             ]
    
     countDoneItems : Model -> Int
     countDoneItems model = 
         List.filter itemIsDone model.todoList
         |> List.length
    
     itemIsDone : TodoModel -> Bool
     itemIsDone todoModel = todoModel.done 
    
     viewList : Model -> Html Msg
     viewList model = 
         (ul [] 
             (List.map Todo.view model.todoList))
             |> Html.map TodoMsg
    
    

    모델에 nextID 속성 (다음의 Tot의 ID)을 추가하고 있습니다.
    다른 수정 사항은 Todo.elm의 모델 변경과 관련이 있습니다.

    update의 인수의 형태를 일부 변경했습니다.
    TodoModel 형을 String 형으로 변경한 것으로, AddNew 의 코드의 전망이 나빠졌기 때문에, let-in문으로 재작성했습니다. nextID 증분 처리를 추가하고 있습니다.

    Main.elm 변경


  • 전체 파일
  • 
     module Main exposing (..)
    
    -import Html exposing (Html, program)
    +import Html exposing (..)
     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 (Todo.new False model.todoCreator.inputStr False) model.todoList
    +                    TodoList.update subMsg model.todoCreator.inputStr model.todoList
                 in
                     ( { model | todoList = updatedTodoListModel }, Cmd.map TodoListMsg todoListCmd )
    
     -- subscription
     subscriptions : Model -> Sub Msg
     subscriptions model =
         Sub.none
    
     -- view
     view : Model -> Html Msg
     view model =
    -    Html.div []
    -        [ Html.map TodoCreatorMsg (TodoCreator.view model.todoCreator)
    -        , Html.map TodoListMsg (TodoList.view model.todoList) 
    -        ]
    +    body [] 
    +        [ h1 [] [ text "ToDo list" ]
    +        , div [] 
    +            [ map TodoCreatorMsg (TodoCreator.view model.todoCreator)
    +            , map TodoListMsg (TodoList.view model.todoList) 
    +            ]
    +    ]
    

    TodoList의 메세지의 인수의 형태가 TodoModel형으로부터 String형으로 변경되었으므로,TodoList.update의 인수는 todoCreator의 inputStr를 그대로 건네줄 수가 있게 되었습니다.
    그 결과, Todo를 import하지 않고 끝나게 되어,Todo의 변경에 영향을 받지 않게 되었습니다.

    보기의 변경은 주로 보기 좋은 변화뿐입니다.

    실행 결과



    이전과 마찬가지로 make하고 만든 index.html을 브라우저에서 봅니다.
    $ elm-make Main.elm --output index.html --debug
    



    이번에는 내부 실장 변경이 주였기 때문에, 외형은 그다지 변하지 않습니다.

    결론



    5회에 걸쳐 연재해 온 Elm 0.18로 만드는 Todo 앱입니다만, 이번이 최종회입니다.

    elm에 처음 만져 tutorial 등을 참고로 작성해 보았습니다.
    Add 버튼이 TodoList 측에 있는 곳(가능하면 TodoCreator 측에 버튼 설치하고 싶다) 등 설계 우려가 되는 곳은 있습니다만, JavaScript를 건드리지 않고 여기까지 만들 수 있는 것은 대단하다고 생각했습니다.

    Elm은 자바스크립트와 연동시킬 수 있는 것 같습니다만, 거기까지 손이 돌지 않았기 때문에, 서서히 공부해 나가고 싶습니다.

    좋은 웹페이지 즐겨찾기