Я пытаюсь создать пользовательский интерфейс, который позволяет пользователю манипулировать рекурсивной структурой данных. Например, представьте себе визуальный редактор схем или редактор таблиц базы данных, в котором у вас есть простые старые типы (строки и целые числа) и составные типы, состоящие из этих простых типов (массивы, структуры). В приведенном ниже примере 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
ТеперьДля создания пользовательского интерфейса я могу сделать простую рекурсивную функцию:
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
s будет приспосабливать пользовательские правки к этой структуре, добавлять новые поля, подполя и редактировать их свойства? Как мне правильно декодировать это в моем цикле 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
, например, будет содержать только новую строку, введенную пользователем, но недостаточно информации для адресации глубоко вложенного элемента.