mirror of
https://github.com/dillonkearns/elm-pages-v3-beta.git
synced 2024-11-26 04:31:39 +03:00
Add FieldView.valueButton helpers, and rerun loaders when query params are changed to ensure that GET form submissions reload data.
This commit is contained in:
parent
df0489fc38
commit
ba875d789f
11
cypress/e2e/get-forms.cy.ts
Normal file
11
cypress/e2e/get-forms.cy.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
context("dev server with base path", () => {
|
||||||
|
it("submits a form to receive ActionData", () => {
|
||||||
|
cy.visit("/get-form");
|
||||||
|
cy.contains("Page 2").click();
|
||||||
|
cy.contains("Current page: 2");
|
||||||
|
cy.contains("Page 1").click();
|
||||||
|
cy.contains("Current page: 1");
|
||||||
|
cy.contains("Page 2").click();
|
||||||
|
cy.contains("Current page: 2");
|
||||||
|
});
|
||||||
|
});
|
143
examples/end-to-end/app/Route/GetForm.elm
Normal file
143
examples/end-to-end/app/Route/GetForm.elm
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
module Route.GetForm exposing (ActionData, Data, Model, Msg, route)
|
||||||
|
|
||||||
|
import BackendTask exposing (BackendTask)
|
||||||
|
import ErrorPage exposing (ErrorPage)
|
||||||
|
import FatalError exposing (FatalError)
|
||||||
|
import Form
|
||||||
|
import Form.Field as Field
|
||||||
|
import Form.FieldView
|
||||||
|
import Form.Validation as Validation
|
||||||
|
import Head
|
||||||
|
import Html exposing (Html)
|
||||||
|
import Html.Attributes as Attr
|
||||||
|
import Html.Styled
|
||||||
|
import Pages.Msg
|
||||||
|
import Pages.PageUrl exposing (PageUrl)
|
||||||
|
import RouteBuilder exposing (StatelessRoute, StaticPayload)
|
||||||
|
import Server.Request as Request exposing (Parser)
|
||||||
|
import Server.Response
|
||||||
|
import Shared
|
||||||
|
import View exposing (View)
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
type alias Msg =
|
||||||
|
()
|
||||||
|
|
||||||
|
|
||||||
|
type alias RouteParams =
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
type alias ActionData =
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
type alias Filters =
|
||||||
|
{ page : Int
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
form : Form.HtmlForm String Filters Filters Msg
|
||||||
|
form =
|
||||||
|
Form.init
|
||||||
|
(\page ->
|
||||||
|
{ combine =
|
||||||
|
Validation.succeed Filters
|
||||||
|
|> Validation.andMap page
|
||||||
|
, view =
|
||||||
|
\formState ->
|
||||||
|
[ page
|
||||||
|
|> Form.FieldView.valueButton "1"
|
||||||
|
[]
|
||||||
|
[ Html.text "Page 1" ]
|
||||||
|
, page
|
||||||
|
|> Form.FieldView.valueButton "2"
|
||||||
|
[]
|
||||||
|
[ Html.text "Page 2" ]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|> Form.field "page"
|
||||||
|
(Field.int { invalid = \_ -> "" }
|
||||||
|
|> Field.map (Maybe.withDefault 1)
|
||||||
|
--|> Field.withInitialValue (.first >> Form.Value.string)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
route : StatelessRoute RouteParams Data ActionData
|
||||||
|
route =
|
||||||
|
RouteBuilder.serverRender
|
||||||
|
{ head = head
|
||||||
|
, data = data
|
||||||
|
, action = action
|
||||||
|
}
|
||||||
|
|> RouteBuilder.buildNoState { view = view }
|
||||||
|
|
||||||
|
|
||||||
|
type alias Data =
|
||||||
|
{ filters : Filters
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data : RouteParams -> Parser (BackendTask FatalError (Server.Response.Response Data ErrorPage))
|
||||||
|
data routeParams =
|
||||||
|
Request.formData (Form.initCombined identity form)
|
||||||
|
|> Request.map
|
||||||
|
(\( formResponse, formResult ) ->
|
||||||
|
case formResult of
|
||||||
|
Ok filters ->
|
||||||
|
Data filters
|
||||||
|
|> Server.Response.render
|
||||||
|
|> BackendTask.succeed
|
||||||
|
|
||||||
|
Err _ ->
|
||||||
|
Data { page = 1 }
|
||||||
|
|> Server.Response.render
|
||||||
|
|> BackendTask.succeed
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
action : RouteParams -> Parser (BackendTask FatalError (Server.Response.Response ActionData ErrorPage))
|
||||||
|
action routeParams =
|
||||||
|
Request.succeed
|
||||||
|
(Server.Response.render {}
|
||||||
|
|> BackendTask.succeed
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
head :
|
||||||
|
StaticPayload Data ActionData RouteParams
|
||||||
|
-> List Head.Tag
|
||||||
|
head static =
|
||||||
|
[]
|
||||||
|
|
||||||
|
|
||||||
|
view :
|
||||||
|
Maybe PageUrl
|
||||||
|
-> Shared.Model
|
||||||
|
-> StaticPayload Data ActionData RouteParams
|
||||||
|
-> View (Pages.Msg.Msg Msg)
|
||||||
|
view maybeUrl sharedModel app =
|
||||||
|
{ title = "GET Form Example"
|
||||||
|
, body =
|
||||||
|
[ form
|
||||||
|
|> Form.toDynamicTransition "user-form"
|
||||||
|
|> Form.withGetMethod
|
||||||
|
|> Form.renderHtml
|
||||||
|
[ Attr.style "display" "flex"
|
||||||
|
, Attr.style "flex-direction" "column"
|
||||||
|
, Attr.style "gap" "20px"
|
||||||
|
]
|
||||||
|
(\_ -> Nothing)
|
||||||
|
app
|
||||||
|
app.data.filters
|
||||||
|
, Html.h2 []
|
||||||
|
[ Html.text <| "Current page: " ++ String.fromInt app.data.filters.page
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|> List.map Html.Styled.fromUnstyled
|
||||||
|
}
|
@ -1,16 +1,16 @@
|
|||||||
module Form.FieldView exposing
|
module Form.FieldView exposing
|
||||||
( Input(..), InputType(..), Options(..), input, inputTypeToString, radio, toHtmlProperties, Hidden(..), select
|
( Input(..), InputType(..), Options(..), input, inputTypeToString, radio, toHtmlProperties, Hidden(..), select, valueButton
|
||||||
, radioStyled, inputStyled
|
, radioStyled, inputStyled, valueButtonStyled
|
||||||
)
|
)
|
||||||
|
|
||||||
{-|
|
{-|
|
||||||
|
|
||||||
@docs Input, InputType, Options, input, inputTypeToString, radio, toHtmlProperties, Hidden, select
|
@docs Input, InputType, Options, input, inputTypeToString, radio, toHtmlProperties, Hidden, select, valueButton
|
||||||
|
|
||||||
|
|
||||||
## Html.Styled Helpers
|
## Html.Styled Helpers
|
||||||
|
|
||||||
@docs radioStyled, inputStyled
|
@docs radioStyled, inputStyled, valueButtonStyled
|
||||||
|
|
||||||
-}
|
-}
|
||||||
|
|
||||||
@ -103,6 +103,90 @@ type Options a
|
|||||||
= Options (String -> Maybe a) (List String)
|
= Options (String -> Maybe a) (List String)
|
||||||
|
|
||||||
|
|
||||||
|
{-| Gives you a submit button that will submit the form with a specific value for the given Field.
|
||||||
|
-}
|
||||||
|
valueButton :
|
||||||
|
String
|
||||||
|
-> List (Html.Attribute msg)
|
||||||
|
-> List (Html msg)
|
||||||
|
-> Form.Validation.Field error parsed Input
|
||||||
|
-> Html msg
|
||||||
|
valueButton exactValue attrs children (Validation viewField fieldName _) =
|
||||||
|
let
|
||||||
|
justViewField : ViewField Input
|
||||||
|
justViewField =
|
||||||
|
expectViewField viewField
|
||||||
|
|
||||||
|
rawField : { name : String, value : Maybe String, kind : ( Input, List ( String, Encode.Value ) ) }
|
||||||
|
rawField =
|
||||||
|
{ name = fieldName |> Maybe.withDefault ""
|
||||||
|
, value = Just exactValue --justViewField.value
|
||||||
|
, kind = justViewField.kind
|
||||||
|
}
|
||||||
|
in
|
||||||
|
case rawField.kind of
|
||||||
|
( Input inputType, properties ) ->
|
||||||
|
Html.button
|
||||||
|
(attrs
|
||||||
|
++ toHtmlProperties properties
|
||||||
|
++ [ (case inputType of
|
||||||
|
Checkbox ->
|
||||||
|
Attr.checked ((rawField.value |> Maybe.withDefault "") == "on")
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Attr.value (rawField.value |> Maybe.withDefault "")
|
||||||
|
-- TODO is this an okay default?
|
||||||
|
)
|
||||||
|
, Attr.name rawField.name
|
||||||
|
, inputType |> inputTypeToString |> Attr.type_
|
||||||
|
]
|
||||||
|
)
|
||||||
|
children
|
||||||
|
|
||||||
|
|
||||||
|
{-| Gives you a submit button that will submit the form with a specific value for the given Field.
|
||||||
|
-}
|
||||||
|
valueButtonStyled :
|
||||||
|
String
|
||||||
|
-> List (Html.Styled.Attribute msg)
|
||||||
|
-> List (Html.Styled.Html msg)
|
||||||
|
-> Form.Validation.Field error parsed Input
|
||||||
|
-> Html.Styled.Html msg
|
||||||
|
valueButtonStyled exactValue attrs children (Validation viewField fieldName _) =
|
||||||
|
let
|
||||||
|
justViewField : ViewField Input
|
||||||
|
justViewField =
|
||||||
|
expectViewField viewField
|
||||||
|
|
||||||
|
rawField : { name : String, value : Maybe String, kind : ( Input, List ( String, Encode.Value ) ) }
|
||||||
|
rawField =
|
||||||
|
{ name = fieldName |> Maybe.withDefault ""
|
||||||
|
, value = Just exactValue
|
||||||
|
, kind = justViewField.kind
|
||||||
|
}
|
||||||
|
in
|
||||||
|
case rawField.kind of
|
||||||
|
( Input inputType, properties ) ->
|
||||||
|
Html.Styled.button
|
||||||
|
(attrs
|
||||||
|
++ (toHtmlProperties properties |> List.map StyledAttr.fromUnstyled)
|
||||||
|
++ ([ (case inputType of
|
||||||
|
Checkbox ->
|
||||||
|
Attr.checked ((rawField.value |> Maybe.withDefault "") == "on")
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Attr.value (rawField.value |> Maybe.withDefault "")
|
||||||
|
-- TODO is this an okay default?
|
||||||
|
)
|
||||||
|
, Attr.name rawField.name
|
||||||
|
, inputType |> inputTypeToString |> Attr.type_
|
||||||
|
]
|
||||||
|
|> List.map StyledAttr.fromUnstyled
|
||||||
|
)
|
||||||
|
)
|
||||||
|
children
|
||||||
|
|
||||||
|
|
||||||
{-| -}
|
{-| -}
|
||||||
input :
|
input :
|
||||||
List (Html.Attribute msg)
|
List (Html.Attribute msg)
|
||||||
|
@ -414,7 +414,7 @@ update config appMsg model =
|
|||||||
model
|
model
|
||||||
|
|
||||||
Nothing ->
|
Nothing ->
|
||||||
if model.url.path == url.path then
|
if model.url.path == url.path && model.url.query == url.query then
|
||||||
( { model
|
( { model
|
||||||
| -- update the URL in case query params or fragment changed
|
| -- update the URL in case query params or fragment changed
|
||||||
url = url
|
url = url
|
||||||
|
Loading…
Reference in New Issue
Block a user