Elm 0.18로 만드는 Todo 앱(4)

9742 단어 Elm
지금까지의 복습

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

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

이번 테마



TodoList측의 기능 확장을 한다. 구체적으로 다음 기능을 추가합니다.
기능 1. 하나의 Todo를 삭제할 수 있도록 한다
기능 2. 완료된 Todo 수를 표시할 수 있도록 한다
기능 3. 완료된 Todo를 삭제하여 미완료 목록을 표시할 수 있도록 합니다.

파일 구성


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

  • 구현



    Todo.elm 수정 (기능 1에 대응)


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

    삭제 상태를 관리하기 위한 속성(del)과 삭제 메시지(onDelete)를 추가합니다.
  • 업데이트 수정
  • 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
    
    +        OnDelete s -> 
    +            if s == model.item then
    +                { model | del = True }
    +            else
    +                model
    

    onDelete의 경우를 추가하고 인수로 Todo 상당의 문자열을 받고 해당 Todo만 상태를 변경하도록 합니다.
  • view 정의
  • view : Model -> Html Msg
    view model = 
        li [] 
            [ 
                checkbox (ToggleDone model.item) model
            ]
    
    -checkbox : msg -> Model -> Html msg
    +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 -> 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" ]
    +        ]
    

    msg 형식 수정과 viewDeleteButton 함수를 추가하고 있습니다.
    버튼을 누를 때 onDelete 메시지를 호출합니다.

    하나의 Todo를 관리하는 Todo.elm에서 자신의 상태를 변경하기 만하면됩니다.
    목록에서 Todo를 제거하는 것은 TodoList 측에서 처리합니다.

    TodoList.elm 변경 (기능 2, 3에 대응)


  • 모델 변경
  •  initialModel : Model
     initialModel = 
         { todoList = 
    -        [ Todo.new False "task1"
    -        , Todo.new False "task2"
    -        , Todo.new True "task3"
    -        , Todo.new False "task4"
    +        [ Todo.new False "task1" False
    +        , Todo.new False "task2" False
    +        , Todo.new True "task3" False
    +        , Todo.new False "task4" False
             ]
         }
    
     type Msg
         = NoOp
         | AddNew
    +    | DeleteFinished
         | TodoMsg Todo.Msg
    

    Todo.elm의 모델 변경에 의한 수정과 기능 3.의 대응을 위해 메시지(DeleteFinished)를 추가하고 있습니다.
  • 업데이트 변경
  •  update : Msg -> TodoModel -> Model -> ( Model, Cmd Msg )
     update message todo model =
         case message of
             NoOp ->
                 model ! []
             AddNew -> 
                 { model | todoList = model.todoList ++ [todo] } ! []
    
    +        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 = updatedTodoList } ! []
    +                { model | 
    +                    todoList = List.filter itemIsNotDeleted updatedTodoList } ! []
    

    List의 표준 함수를 사용하여 List의 요소에서 필터링 된 것만을 검색합니다.

    DeleteFinished에서는 totModel의 done이 False의 것만을 필터링합니다.

    TodoMsg에서는 let문으로 Todo에 갱신의 위양을 한 후의 리스트를 작성해, in문으로 갱신후의 리스트에 대해서 todoModel의 del이 False의 것만을 필터링하고 있습니다.
  • 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
    

    viewCounter 함수에서는 Todo의 (완료수/총수)라는 표시를 하고 있습니다.
    deleteFinishedButton 함수는 버튼 누름으로 DeleteFinished 메시지를 호출합니다.

    Main.elm 변경 (기능 1에 대응)


  • 전체 파일
  •  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 (Todo.new False model.todoCreator.inputStr) model.todoList
    +                    TodoList.update subMsg (Todo.new False model.todoCreator.inputStr False) 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) 
             ]
    

    Todo.elm의 모델 변경에 따라 수정되었습니다.

    실행 결과



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



    Finished Task에서 Todo의 수가 표시되어 DeleteFinished 버튼이나 × 버튼이 있고, 누르면 Todo가 삭제되는 것을 알 수 있다고 생각합니다.

    결론



    주로 TodoList에서 세 가지 기능을 추가해 보았습니다.
    다만 현재의 구현이라고 Todo 모델의 item 속성으로 update 하는 Todo를 결정하고 있기 때문에, 같은 item 속성(Todo명)의 요소의 경우, 함께 update 되어 버린다고 하는 문제가 있습니다.

    다음 번(아마 최종회)은 유일하게 update 하는Todo를 결정할 수 있도록(듯이) 기능 확장을 해, 조금 보기 좋게 정돈하고 싶습니다.
    다음 번 : Elm 0.18로 만드는 Todo 앱 (5)

    좋은 웹페이지 즐겨찾기