Динамическая форма с composable-form

#elm

#elm

Вопрос:

Я пытаюсь реализовать динамическую форму в Elm 0.19, используя hecrj / composable-form.

Я получаю json с полями, их описаниями и т. Д., Поэтому я заранее не знаю, сколько в нем будет полей.

Итак, традиционный способ определения формы:

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

не работает, потому что я не знаю OutputValues структуру заранее.

Я видел, что есть функция Form.list , которая выглядит как многообещающий путь, хотя, похоже, она ожидает, что все поля равны, что не в моем случае, например, у меня может быть текстовое поле и поле выбора.

Есть ли какой-нибудь прямой способ сделать это с помощью этой библиотеки? Спасибо.

Ответ №1:

Библиотека форм явно не поддерживает то, что вы пытаетесь сделать, но мы можем заставить ее работать!

tldr;

Вот мой пример того, как вы можете использовать JSON и создать форму: https://ellie-app.com/bJqNh29qnsva1

Как туда добраться

Form.list безусловно, многообещающий путь. Вы также совершенно правы, что Form.list требует, чтобы все поля были одного типа. Итак, давайте начнем с этого! Мы можем создать одну структуру данных, которая может их содержать, создав пользовательский тип. В моем примере я вызвал это DynamicFormFieldValue . Мы создадим вариант для каждого типа поля. Я создал их для текста, целых чисел и списка выбора. Каждая из них должна содержать значение поля и все дополнительные параметры (например, заголовок и значение по умолчанию), чтобы оно отображалось красиво. Это будет то, во что мы декодируем 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 изменять форму на основе значения. Итак, мы сопоставим шаблон с пользовательским типом и вернем Form.textField , Form.numberField , или Form.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 не был разработан с учетом варианта использования. Мы хотим, чтобы список оставался той же длины и просто повторялся. Для достижения этой цели мы удалим кнопки «добавить» и «удалить» и будем вынуждены указать фиктивное значение по умолчанию (которое никогда не будет использовано).

 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 продемонстрирует остальное, и вы сможете адаптировать его к своим потребностям!

Комментарии:

1. Кроме того, если некоторые поля являются обязательными или нет, они также могут быть обработаны, как показано здесь: ellie-app.com/bMvRDyhLDF8a1