我正在尝试构建一个允许用户操纵递归数据结构的UI。例如,想象一个可视化模式编辑器或数据库表编辑器,其中您具有普通的旧类型(字符串和整数)以及由这些普通类型(数组,结构)组成的复合类型。在下面的示例中,Struct_
类似于JavaScript对象,其中的键是字符串,值是任何类型,包括嵌套的Array_
s和Struct_
s。
-- underscores appended to prevent confusion about native Elm types. These are custom to my application.
type ValueType
= String_
| Int_
| Float_
| Array_ ValueType
| Struct_ (List (String, ValueType))
type alias Field =
{ id : Int
, label : String
, hint : String
, hidden : Bool
, valueType : ValueType
}
type alias Schema = List Field
现在要为此构建一个UI,我可以创建一个简单的递归函数:
viewField : Field -> Html Msg
viewField field =
div []
[ input [ type_ "text", value field.label ] []
, viewValueType field.valueType
]
viewValueType : ValueType -> Html Msg
viewValueType valueType =
let
structField : (String, ValueType) -> Html Msg
structField (key, subtype) =
div []
[ input [type_ "text", placeholder "Key", value key, onInput EditStructSubfieldKey] []
, viewValueType subtype
]
options : List(Html Msg)
options = case valueType of
String_ -> -- string ui
Int_ -> -- int ui
Float_ -> -- float ui
Array_ subtype ->
[ label [] [ text "subtype" ]
, viewValueType subtype
]
Struct_ fields ->
[ label [] [ text "subfields" ]
, List.map structField fields
, button [ onClick AddStructSubfield ] [ text "Add subfield" ]
]
in
div [] options
当尝试使用此递归结构操纵我的状态时,就会出现我的问题。 Msg
中的哪种数据结构可以容纳用户对该结构的编辑,添加新字段,子字段以及编辑其属性?如何在update
循环中正确解码?
例如...
type alias Model =
{ fields : List Field }
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
AddStructSubfield _???_ ->
({model | fields = ???}, Cmd.none)
EditStructSubfieldKey _???_ ->
({model | fields = ???}, Cmd.none)
[您将附加到AddStructSubfield
或EditStructSubfieldKey
消息(随同onClick
处理程序传递给上述button
的消息)什么样的数据,以正确更新您的状态,特别是当说Struct_
时,嵌套在另一个Struct_
中,嵌套在Array_
中?例如,EditStructSubfieldKey
将仅包含用户输入的新字符串,但没有足够的信息来解决嵌套的项目。
我们在代码库中完全做到了这一点,但是还没有开源支持该功能的'库'。但是,对于您的问题的答案是,您需要在代码和消息中添加Path
的概念。
type Path
= Field: String
| Index: Int
然后,当您下降[Field "f1", Index 3, ...]
时,您的视图必须不断更新路径,并且您的更新功能需要通过插入,删除...来支持,这些插入,删除,...采用路径和现有结构并返回给您新的结构。] >