Кирпичные формы с динамическими полями

В Brick.Forms способ создания новой формы заключается в том, что у вас есть тип данных Haskell с некоторыми заранее определенными полями, которые затем становятся соответствующими входными данными в кирпичной форме.

Этот модуль предоставляет API формы ввода. Этот API позволяет вам создайте интерфейс ввода на основе выбранного вами типа данных. Каждый ввод в форме соответствует полю вашего типа данных. Этот API затем автоматически отправляет события ввода с клавиатуры и мыши каждому поле ввода формы, управляет отрисовкой формы, уведомляет пользователя когда значение поля формы недействительно, и сохраняет действительные входные данные в вашем тип данных, когда это возможно

У меня есть требование, например, список имен столбцов из таблицы БД, и я хочу создать форму кирпича для вставки новой строки в таблицу БД. Поскольку я мог выбрать любую таблицу БД во время выполнения, имена столбцов могут меняться во время выполнения. Поэтому в лучшем случае тип данных, который я могу иметь, — это тонкая оболочка списка имен столбцов.

Как я могу сделать кирпичную форму с таким типом данных? Для простоты предположим, что форма для всех отображаемых полей ввода принимает только строковые значения или текстовые значения.

Если я не могу создать форму с таким типом данных, каковы альтернативы?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
121
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я взял bricks FormDemo.hs, удалил ненужные фрагменты и изменил его оттуда. Код зависит от containers и microlens-ghc. Возможно, есть более чистое решение, но это работает.

Вместо использования типа записи, как обычно, мы используем Map. Важная часть — at, которая дает линзу для нашей карты. Один довольно досадный недостаток заключается в том, что мы получаем Maybe повсюду, но у меня такое ощущение, что мы не сможем избавиться от него типобезопасным способом без зависимых типов.

Еще одним отличием от оригинальной демонстрации является наша специальная функция editMaybeTextField, которая необходима для обработки значений Maybe.

{-# LANGUAGE OverloadedStrings #-}

module Main where

import Data.Text qualified as T
import Lens.Micro (Lens')

import Graphics.Vty qualified as V
import Graphics.Vty.Platform.Unix (mkVty)

import Brick
import Brick.Focus (
    focusRingCursor,
 )
import Brick.Forms (
    Form,
    FormFieldState,
    editField,
    focusedFormInputAttr,
    formFocus,
    formState,
    handleFormEvent,
    invalidFormInputAttr,
    newForm,
    renderForm,
    (@@=),
 )
import Brick.Widgets.Edit qualified as E
import Data.Map
import Data.Maybe (fromMaybe)
import Lens.Micro.GHC (at)

type FieldName = T.Text
type Properties = Map T.Text T.Text

makeFieldLens :: T.Text -> Lens' Properties (Maybe T.Text)
makeFieldLens = at @Properties

mkForm :: [FieldName] -> Properties -> Form Properties e (Maybe T.Text)
mkForm fieldNames =
    let label s w = padBottom (Pad 1) (vLimit 1 $ hLimit 15 $ txt s <+> fill ' ') <+> w
        createField :: FieldName -> Properties -> FormFieldState Properties e (Maybe T.Text)
        createField fieldName = label fieldName @@= editMaybeTextField (makeFieldLens fieldName) (Just fieldName) (Just 1)
     in newForm (fmap createField fieldNames)

editMaybeTextField ::
    (Ord n, Show n) =>
    Lens' s (Maybe T.Text) ->
    n ->
    Maybe Int ->
    s ->
    FormFieldState s e n
editMaybeTextField stLens n limit =
    let ini = fromMaybe ""
        val = Just . Just . T.intercalate "\n"
        renderText = txt . T.intercalate "\n"
     in editField stLens n limit ini val renderText id

theMap :: AttrMap
theMap =
    attrMap
        V.defAttr
        [ (E.editAttr, V.white `on` V.black)
        , (E.editFocusedAttr, V.black `on` V.yellow)
        , (invalidFormInputAttr, V.white `on` V.red)
        , (focusedFormInputAttr, V.black `on` V.yellow)
        ]

draw :: Form Properties e (Maybe T.Text) -> [Widget (Maybe T.Text)]
draw f = [renderForm f]

app :: App (Form Properties e (Maybe T.Text)) e (Maybe T.Text)
app =
    App
        { appDraw = draw
        , appHandleEvent = \ev -> do
            case ev of
                VtyEvent (V.EvKey V.KEsc []) -> halt
                _ -> handleFormEvent ev
        , appChooseCursor = focusRingCursor formFocus
        , appStartEvent = return ()
        , appAttrMap = const theMap
        }

getFieldNames :: IO [T.Text]
getFieldNames = pure ["something", "somethingelse"]

main :: IO ()
main = do
    fieldNames <- getFieldNames

    let buildVty = mkVty V.defaultConfig
        initialValues = fromList (fmap (,"") fieldNames)
        f = mkForm fieldNames initialValues

    initialVty <- buildVty
    f' <- customMain initialVty buildVty Nothing app f
    print $ formState f'

Не могли бы вы рассказать мне, как вы к этому пришли? например, каков был мыслительный процесс, потому что я прочитал это и подумал, что, может быть, я застрял.

kush87 05.04.2024 17:11

@kush87, я сначала установил тип формы такой, какой хотел. Затем я параметризовал mkForm именами полей. После этого я удалил статический список полей и заменил его на fmap, а затем начал создавать функцию createField на основе того, что было в исходном примере. После этого я просто следил за типами, и это привело меня к компиляционной программе после некоторых почесывания головы. Общие советы: используйте типизированные дыры, внимательно читайте ошибки типов и пытайтесь понять, почему компилятор хочет того, чего он хочет, используйте подсказки типов, чтобы превратить переменные типа в конкретные типы.

ollimandoliini 06.04.2024 19:31

Другие вопросы по теме