如何在Elm中为嵌套记录创建通用更新函数

时间:2016-07-23 18:24:10

标签: elm

在Elm中,我有一个具有嵌套属性的模型,如:

model =
  { name = ""
  , disruptedFields =
    { advertising =
      { name = "Advertising"
      , checked = False
      }
    , travel =
      { name = "Travel"
      , checked = False
      }
    , utilities =
      { name = "Utilities"
      , checked = False
      }
    }
  }

disruptedFields包含复选框的值列表。当我点击复选框时,我向UpdateDisruptedField发送了一条更新消息,目前看起来像是:

UpdateDisruptedField value ->
  let
    fieldCollection = model.disruptedFields
    field = fieldCollection.advertising
  in
    { model | disruptedFields =
      { fieldCollection | advertising =
        { field | checked = (not value) }
      }
    }

我的更新功能已硬编码到model.disruptedField.advertisingfield变量中的advertising。这对我有用,但我不知道这个函数是通用的。

如何将记录传递到UpdateDisruptedField以便我可以将其设为通用?

2 个答案:

答案 0 :(得分:6)

这是具有许多输入字段的Elm应用程序的常见问题。创建通用更新函数有两种方法可以减少代码重复。

  1. 使用输入标识符扩展更新消息,然后使用case checkboxType of添加另一级别的开关,并使用所有嵌套记录处理更新。每次在模型中添加新字段时,都必须使用额外的分支扩展更新以处理更新。

  2. 使用词典重新构建模型,并以适当的通用方式处理更新。

  3. 我更喜欢第二个选项,因为更新嵌套的记录是一件苦差事。

    请考虑以下示例:

    module Main exposing (..)
    
    import Html exposing (div, input, text, label)
    import Html.App exposing (beginnerProgram)
    import Html.Events exposing (onCheck)
    import Html.Attributes exposing (type', checked)
    import Dict
    
    
    (=>) : a -> b -> ( a, b )
    (=>) a b =
        ( a, b )
    
    
    main =
        beginnerProgram { model = model, view = view, update = update }
    
    
    model =
        { name = ""
        , disruptedFields =
            Dict.fromList
                [ "advertising"
                    => { name = "Advertising"
                       , checked = False
                       }
                , "travel"
                    => { name = "Travel"
                       , checked = False
                       }
                , "utilities"
                    => { name = "Utilities"
                       , checked = False
                       }
                ]
        }
    
    
    type Msg
        = Check String Bool
    
    
    view model =
        let
            checkbox ( key, data ) =
                label []
                    [ text data.name
                    , input
                        [ type' "checkbox"
                        , checked data.checked
                        , onCheck (Check key)
                        ]
                        []
                    ]
        in
            div []
                (model.disruptedFields
                    |> Dict.toList
                    |> List.map checkbox
                )
    
    
    update msg model =
        case msg of
            Check checkboxId checked ->
                let
                    updateRecord =
                        Maybe.map (\checkboxData -> { checkboxData | checked = checked })
    
                    disruptedFieldsUpdated =
                        Dict.update checkboxId
                            updateRecord
                            model.disruptedFields
                in
                    { model | disruptedFields = disruptedFieldsUpdated }
    

    请注意,我一直在使用=>运算符让元组看起来更漂亮。

    disruptedFields现在是一个词典,使用String键来识别每个复选框。

答案 1 :(得分:1)

为什么不使用Dict而不是明确列出所有字段?