Reputation: 27217
I have a very simple login form with username / password. Is there a way to POST this form to the server and check for a 200s response without having to go through the coding gymnastics required to track the value of each field character by character as the user types? (Yes, I understand that it is the Elm way to do things, but adding two fields to my model, two Msg with accompanying entries in update just seems like a lot of unneeded code for a simple login form, and storing the password in the model forever seems like a bad idea.)
I have read these two questions, and I have found the onSubmit
event. However, I am not sure what to do once update
receives the message that the onSubmit
event has fired. I feel like there might be two ways forward and I can't figure out how to do either:
I would use the default submit function, but I would prefer to login with an asynchronous request and not leave the page.
Upvotes: 4
Views: 1008
Reputation: 4777
Although I would do the gymnastics for this case, you can do another gymnastics with JSON decoder to get values from DOM and put them into a Msg. JSON decoder allows you to get any values from event object with JSON decoder as long as it’s property access instead of method call.
Another trick is that you can access input elements by name from the form element.
import Html exposing (Html)
import Html.Attributes as HA
import Html.Events as HE
import Json.Decode as Json exposing (Decoder)
type Msg
= SubmitLogin String String
update : Msg -> Model -> Model
update msg model =
case msg of
SubmitLogin username password ->
-- Do AJAX with username and password
decodeField : String -> Decoder String
decodeField name =
Json.at
[ "currentTarget"
, name
, "value"
]
Json.string
decodeLoginForm : Decoder Msg
decodeLoginForm =
Json.map2
SubmitLogin
(decodeField "username")
(decodeField "password")
preventDefault : HE.Options
preventDefault =
{ preventDefault = True
, stopPropagation = False
}
onLoginSubmit : Html.Attribute Msg
onLoginSubmit =
HE.onWithOptions
"submit"
preventDefault
decodeLoginForm
view : Model -> Html.Html Msg
view model =
Html.form
[ onLoginSubmit ]
[ Html.input
[ HA.name "username"
, HA.type_ "text"
]
[]
, Html.input
[ HA.name "password"
, HA.type_ "password"
]
[]
, Html.input
[ HA.type_ "submit"
, HA.value "Login"
]
[]
]
Upvotes: 3
Reputation: 13692
I had the same issue here, and I also found elm
to be tedious when submitting a form,
Assuming this is your view:
import Html exposing (..)
import Html.Attributes exposing (..)
view : Model -> Html Msg
view model =
Html.form [method "POST", action "/address"]
[ input [ type_ "text" ,name "user"] [],
input [ type_ "password", name "password"] [],
input [ type_ "submit" ,value "Submit"] []
]
And this is what I came up with:
replace the submit button with:
input [ type_ "button"
,value "Submit"
,attribute "onclick" "javascript:this.form.submit();"] []
Obviously you can mix in whatever javascript you need, if you don't care about the return values of the submission.
I know this is not the "elm way" of doing things, but it simple, pragmatic and it works.
Upvotes: 2
Reputation: 9885
Is there a way to POST this form without having to go through the coding gymnastics required to track the value of each field character by character as the user types?
No. To do so would require pulling the values in the form inputs out of the DOM. Elm prevents you from doing that, so yes you need the glorious gymnastics.
...storing the password in the model forever seems like a bad idea.
You don't need to hold on to the password once you're done with it. There are a number of ways to go about it. You can clear it with an empty String
, or use a Maybe String
, or use a sum type (tags) as the root of your model instead of the typical product type (record) so that you can effectively switch models at run-time. You've got options.
I am not sure what to do once update receives the message that the onSubmit event has fired.
When the onSubmit
fires use the Http module to construct a POST request. If the server will respond with data you need (other that status code) you will need to create a Json decoder so Elm can parse the data and produce a typed value (rather than a glob of stuff).
This process will produce a Request
, which you'll then need to convert to a Cmd Msg
using Http.send
This will give you the opportunity to tell Elm what Msg
to send to update
once the server responds to your request. This Msg
will be of type Result Http.Error YourType
, which you can process with the functions in the Result
module.
Upvotes: 2