Как обрабатывать несколько форм на одной странице, используя Suave.Experimental

Я должен создать простую страницу с несколькими формами. Я решил использовать Suave.Experimental для этой цели. Когда я нажимаю на submit Кнопка на второй форме, я получаю следующую ошибку

Отсутствует поле формы "Второй"

WebPart

let webpart =
    choose [
        GET >=> OK page
        POST >=> bindToForm firstForm firstHandle
        POST >=> bindToForm secondForm secondHandle
    ]

Можно ли иметь несколько форм с Suave.Experimental?

Если так - чего мне здесь не хватало?

Если нет - какой будет подходящий подход для этого?

Минимальный пример ниже:

open Suave.Form
open Suave.Html

type FirstForm = { First : string }
type SecondForm = { Second : decimal }

let firstForm : Form<FirstForm> = 
    Form ([ TextProp ((fun f -> <@ f.First @>), [ ])],[])

let secondForm : Form<SecondForm> = 
    Form ([ DecimalProp ((fun f -> <@ f.Second @>), [])], [])

type Field<'a> = {
    Label : string
    Html : Form<'a> -> Suave.Html.Node
}

type Fieldset<'a> = {
    Legend : string
    Fields : Field<'a> list
}

type FormLayout<'a> = {
    Fieldsets : Fieldset<'a> list
    SubmitText : string
    Form : Form<'a>
}

let form x = tag "form" ["method", "POST"] x
let submitInput value = input ["type", "submit"; "value", value]

let renderForm (layout : FormLayout<_>) =    
    form [
        for set in layout.Fieldsets -> 
            tag "fieldset" [] [
                yield tag "legend" [] [Text set.Legend]

                for field in set.Fields do
                    yield div ["class", "editor-label"] [
                        Text field.Label
                    ]
                    yield div ["class", "editor-field"] [
                        field.Html layout.Form
                    ]
            ]

        yield submitInput layout.SubmitText
    ]

open Suave
open Suave.Filters
open Suave.Operators
open Suave.Successful
open Suave.Web

open Suave.Model.Binding
open Suave.RequestErrors

let bindToForm form handler = 
    bindReq (bindForm form) handler BAD_REQUEST

let firstHandle first = 
    printfn "first = %A" first
    Redirection.FOUND "/"

let secondHandle second = 
    printfn "second = %A" second
    Redirection.FOUND "/"

let page = 
    html [] [
        head [] [ ]
        body [] [
            div [][
                renderForm
                    { Form = firstForm
                      Fieldsets = 
                        [ { Legend = "1"
                            Fields = 
                              [ { Label = "Text"
                                  Html = Form.input (fun f -> <@ f.First @>) []  }
                                  ] }]
                      SubmitText = "Submit" }

                renderForm
                    { Form = secondForm
                      Fieldsets = 
                        [ { Legend = "2"
                            Fields = 
                              [ { Label = "Decimal"
                                  Html = Form.input (fun f -> <@ f.Second @>) [] }
                                  ] }]
                      SubmitText = "Submit" }
            ]
        ]
    ]
    |> htmlToString

let webpart =
    choose [
        GET >=> OK page
        POST >=> bindToForm firstForm firstHandle
        POST >=> bindToForm secondForm secondHandle
    ]

startWebServer defaultConfig webpart

1 ответ

Вы направляете один и тот же маршрут POST / к обоим обработчикам форм. У вас должен быть только один маршрут для каждого, в противном случае выполняется только один из них.

Вы можете установить различное действие формы для каждой из ваших форм и соответственно создать пути для них.

Я быстро внес эти изменения в ваш код, и теперь он должен работать:

open Suave.Form
open Suave.Html

type FirstForm = { First : string }
type SecondForm = { Second : decimal }

let firstForm : Form<FirstForm> = 
    Form ([ TextProp ((fun f -> <@ f.First @>), [ ])],[])

let secondForm : Form<SecondForm> = 
    Form ([ DecimalProp ((fun f -> <@ f.Second @>), [])], [])

type Field<'a> = {
    Label : string
    Html : Form<'a> -> Suave.Html.Node
}

type Fieldset<'a> = {
    Legend : string
    Fields : Field<'a> list
}

type FormLayout<'a> = {
    Fieldsets : Fieldset<'a> list
    SubmitText : string
    Form : Form<'a>
}

let form action x = tag "form" ["method", "POST"; "action", action] x
let submitInput value = input ["type", "submit"; "value", value]

let renderForm action (layout : FormLayout<_>) =    
    form action [
        for set in layout.Fieldsets -> 
            tag "fieldset" [] [
                yield tag "legend" [] [Text set.Legend]

                for field in set.Fields do
                    yield div ["class", "editor-label"] [
                        Text field.Label
                    ]
                    yield div ["class", "editor-field"] [
                        field.Html layout.Form
                    ]
            ]

        yield submitInput layout.SubmitText
    ]

open Suave
open Suave.Filters
open Suave.Operators
open Suave.Successful
open Suave.Web

open Suave.Model.Binding
open Suave.RequestErrors

let bindToForm form handler = 
    bindReq (bindForm form) handler BAD_REQUEST

let firstHandle first = 
    printfn "first = %A" first
    Redirection.FOUND "/"

let secondHandle second = 
    printfn "second = %A" second
    Redirection.FOUND "/"

let page = 
    html [] [
        head [] [ ]
        body [] [
            div [][
                renderForm "first"
                    { Form = firstForm
                      Fieldsets = 
                        [ { Legend = "1"
                            Fields = 
                              [ { Label = "Text"
                                  Html = Form.input (fun f -> <@ f.First @>) []  }
                                  ] }]
                      SubmitText = "Submit" }

                renderForm "second"
                    { Form = secondForm
                      Fieldsets = 
                        [ { Legend = "2"
                            Fields = 
                              [ { Label = "Decimal"
                                  Html = Form.input (fun f -> <@ f.Second @>) [] }
                                  ] }]
                      SubmitText = "Submit" }
            ]
        ]
    ]
    |> htmlToString

let webpart =
    choose [
        GET >=> OK page
        POST >=> choose 
            [ path "/first" >=> (bindToForm firstForm firstHandle)
              path "/second" >=> (bindToForm secondForm secondHandle) ]
    ]

startWebServer defaultConfig webpart
Другие вопросы по тегам