Elm備忘録 ~Html.Keyedの活用[前編]

# Elm備忘録 ~Html.Keyedの活用[前編]~

- type=fileのinput要素で活用する
- type=file, inputの仕様
  - 同じファイルを二度連続で選択したときonChangeが発火しない(changeしてないからその通りっちゃあそのとおり)
- これでは困ることがある
- 別のフォームでの入力値が変わったときにinput要素でたとえ同じファイルでももう一度onChangeしてほしい!
  - 別のフォームでの入力でバリデーションの条件変わったりとかあるでしょ？あったのよ
- Html.Keyedを使って別のフォームの入力値が変わったときにinput要素を再生成させることで切り抜ける

- Html.Keyed.nodeはHtml要素を生成させるための関数(ざっくりいうとね)だが、Html.nodeと違うのは第三引数
  - Html.nodeは List (Html msg)
  - Html.Keyedは List (String, Html msg)
  - になっている。2つとも要素のchildrenを指定できる引数となっている
  - Keyedのほうはchildrenとその各要素対してStringでkeyを指定する
  - keyが指定されるとどうなるのか?VirtualDomのDiffを計算するフェーズでKeyが一致しなければ要素が再生成されることになる
  - なのでfile=typeのinput以外のフォームによる入力値をkeyにすることで、それが変わるとfile=typeのinputは再生されて、同じファイルを選択したときにも再びonChangeが発火する


- before

```elm
type Msg
    = ChangedCategory String
    | SelectedFile String


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        ChangedCategory category ->
            ( { model | selectedCategory = category }, Cmd.none )

        SelectedFile filePath ->
            ( { model | selectedFilePathMessage = Just (filePath ++ "|category:" ++ model.selectedCategory) }, Cmd.none )



---- VIEW ----


filePathMessage : Maybe String -> Html msg
filePathMessage strM =
    strM |> Maybe.withDefault "ファイルが選択されてません" |> Html.text


view : Model -> Html Msg
view model =
    div []
        [ Html.div
            []
            [ Html.input [ onInput SelectedFile, Html.Attributes.type_ "file" ] []
            , model.selectedCategory
            , filePathMessage model.selectedFilePathMessage
            ]
        , Html.div []
            [ Html.text "ファイル種類: "
            , Html.select [ onInput ChangedCategory ]
                [ Html.option [ Html.Attributes.value "csv" ]
                    [ Html.text "csv"
                    ]
                , Html.option [ Html.Attributes.value "tsv" ]
                    [ Html.text "tsv"
                    ]
                , Html.option [ Html.Attributes.value "plain" ]
                    [ Html.text "plain"
                    ]
                ]
            ]
        ]
```

- after

```elm
type Msg
    = ChangedCategory String
    | SelectedFile String


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        ChangedCategory category ->
            ( { model | selectedCategory = category }, Cmd.none )

        SelectedFile filePath ->
            ( { model | selectedFilePathMessage = Just (filePath ++ "|category:" ++ model.selectedCategory) }, Cmd.none )

---- VIEW ----

filePathMessage : Maybe String -> Html msg
filePathMessage strM =
    strM |> Maybe.withDefault "ファイルが選択されてません" |> Html.text

view : Model -> Html Msg
view model =
    div []
        [ Keyed.node "div" -- 変わったよ！
            []
            [ ( model.selectedCategory, Html.input [ onInput SelectedFile, Html.Attributes.type_ "file" ] [] )
            , ( model.selectedCategory, filePathMessage model.selectedFilePathMessage )
            ]
        , Html.div []
            [ Html.text "ファイル種類: "
            , Html.select [ onInput ChangedCategory ]
                [ Html.option [ Html.Attributes.value "csv" ]
                    [ Html.text "csv"
                    ]
                , Html.option [ Html.Attributes.value "tsv" ]
                    [ Html.text "tsv"
                    ]
                , Html.option [ Html.Attributes.value "plain" ]
                    [ Html.text "plain"
                    ]
                ]
            ]
        ]
```