如何更新 Elm 列表中的特定元素

How to update specific element in a list in Elm

有几个 apples(类型为 List),它们将在 Web 视图中显示它们自己。用户可以更新 Apple 的任何 size 属性。我有一个消息类型 UpdateSize,它将通过 onInput.

触发

编辑任何苹果只会触发消息,而不知道 要更新哪个 个苹果。

是否可以将 id 属性传递给 UpdateSize 消息?

感谢您阅读本文,Elm 很棒!

module Main exposing (main)

import Browser
import Html exposing (Html, button, div, text, input)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick,onInput)
import String

type alias Apple = {
   size: Int}

type alias Model = {
    apples: List(Apple)}


initialModel : Model
initialModel =
    { apples = [ Apple 10, Apple 11, Apple 12] }


type Msg
    = UpdateSize String


update : Msg -> Model -> Model
update msg model =
    case msg of
        UpdateSize s -> {model | apples = ??? } -- how to update a single Apple with new size
        _ -> model

viewApple : Apple -> Html Msg
viewApple a =
    input [ type_ "text" ,placeholder ""
          , value (String.fromInt a.size)
          , onInput UpdateSize]
          []



view : Model -> Html Msg
view model =
    div []
        (List.map viewApple model.apples)


main : Program () Model Msg
main =
    Browser.sandbox
        { init = initialModel
        , view = view
        , update = update
        }

代码link:https://ellie-app.com/ghd9jrcjKQQa1

对于您当前的实现,不可能知道要更新哪个苹果,因为没有关于苹果的唯一属性。如果两个苹果大小一样怎么办?如果苹果有 ID 会更好,或者您使用字典类型来跟踪苹果。

但是,为了演示,您可以说苹果的索引列表是唯一的,您可以据此找到它们。在现实生活中,这将是一个脆弱的解决方案。

这是一种使用 List.Extra.

中的一些辅助函数的简单方法
-- ...

type alias Size =
    Int


type Msg
    = UpdateSize Int String


update : Msg -> Model -> Model
update msg model =
    case msg of
        UpdateSize index sizeStr ->
            let
                maybeSize =
                    String.toInt sizeStr
            in
            maybeSize
                |> Maybe.withDefault (\size -> { model | apples = updateApple index size model.apples })
                |> model

        _ ->
            model


updateApple : Int -> Size -> List Apple -> List Apple
updateApple index size apples =
    let
        maybeApple =
            List.Extra.getAt index apples
    in
    maybeApple
        |> Maybe.map (\apple -> List.Extra.setAt index { apple | size = size } apples)
        |> Maybe.withDefault apples

-- ...

viewApple : Int -> Apple -> Html Msg
viewApple index a =
    input
        [ type_ "text"
        , placeholder ""
        , value (String.fromInt a.size)
        , onInput (UpdateSize index)
        ]
        []


view : Model -> Html Msg
view model =
    div []
        (List.indexedMap viewApple model.apples)