具有可组合形式的动态形式

Dynamic form with composable-form

我正在尝试使用 hecrj/composable-form 在 Elm 0.19 中实现动态表单。

我收到一个 json,其中包含字段、它们的描述等,所以我事先不知道它会有多少字段。

所以定义表单的传统方式:

Form.succeed OutputValues
      |> Form.append field1
      |> Form.append field2

不起作用,因为我事先不知道 OutputValues 结构。

我看到有一个函数 Form.list 看起来像是一条很有前途的路径,尽管它似乎期望所有字段都相等,但我的情况并非如此,我可能有一个文本字段和一个 [=例如 27=] 字段。

这个库有没有直接的方法可以做到这一点? 谢谢。

表单库未明确支持您尝试执行的操作,但我们可以让它发挥作用!

tldr;

这是我的示例,说明如何使用 JSON 并创建表单:https://ellie-app.com/bJqNh29qnsva1

如何到达那里

Form.list绝对是有前途的道路。您也完全正确,Form.list 要求所有字段都属于同一类型。让我们从这里开始吧!我们可以通过创建自定义类型来创建一个可以容纳它们的数据结构。在我的例子中,我称之为 DynamicFormFieldValue。我们将为每种字段制作一个变体。我为文本、整数和 select 列表创建了一个。每个人都需要保存字段的值和所有附加项(如标题和默认值)以使其显示得很好。这将是我们将 JSON 解码为什么,表单值是什么,以及表单输出将是什么。结果类型如下所示:

type alias TextFieldRequirements =
    { name : String
    , default : Maybe String
    }


type alias IntFieldRequirements =
    { name : String
    , default : Maybe Int
    }


type alias SelectFieldRequirements =
    { name : String
    , default : Maybe String
    , options : List ( String, String )
    }


type DynamicFormFieldValue
    = TextField String TextFieldRequirements
    | IntField Int IntFieldRequirements
    | SelectField String SelectFieldRequirements

要显示表单,您只需要一个可以获取表单值并显示适当的表单小部件的函数。表单库提供Form.meta根据值改变表单。因此,我们将在自定义类型和 return Form.textFieldForm.numberFieldForm.selectField 上进行模式匹配。像这样:

dynamicFormField : Int -> Form DynamicFormFieldValue DynamicFormFieldValue
dynamicFormField fieldPosition =
    Form.meta
        (\field ->
            case field of
                TextField textValue ({ name } as requirements) ->
                    Form.textField
                        { parser = \_ -> Ok field
                        , value = \_ -> textValue
                        , update = \value oldValue -> TextField value requirements
                        , error = always Nothing
                        , attributes =
                            { label = name
                            , placeholder = ""
                            }
                        }

                IntField intValue ({ name } as requirements) ->
                    Form.numberField
                        { parser = \_ -> Ok field
                        , value = \_ -> String.fromInt intValue
                        , update = \value oldValue -> IntField (Maybe.withDefault intValue (String.toInt value)) requirements
                        , error = always Nothing
                        , attributes =
                            { label = name
                            , placeholder = ""
                            , step = Nothing
                            , min = Nothing
                            , max = Nothing
                            }
                        }

                SelectField selectValue ({ name, options } as requirements) ->
                    Form.selectField
                        { parser = \_ -> Ok field
                        , value = \_ -> selectValue
                        , update = \value oldValue -> SelectField value requirements
                        , error = always Nothing
                        , attributes =
                            { label = name
                            , placeholder = ""
                            , options = options
                            }
                        }
        )

用库连接这个显示函数有点笨拙。 Form.list 在设计时并未考虑 use-case。我们希望列表保持相同的长度并且只是被迭代。为此,我们将删除“添加”和“删除”按钮,并强制提供一个虚拟默认值(永远不会被使用)。

dynamicForm : Form (List DynamicFormFieldValue) (List DynamicFormFieldValue)
dynamicForm =
    Form.list
        { default =
            -- This will never get used
            TextField "" { name = "", default = Nothing }
        , value = \value -> value
        , update = \value oldValue -> value
        , attributes =
            { label = "Dynamic Field Example"
            , add = Nothing
            , delete = Nothing
            }
        }
        dynamicFormField

希望 ellie 示例演示了其余内容,您可以根据需要进行调整!